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:
authorAlexander Romanov <a.romanov@blend4web.com>2017-04-13 12:30:03 +0300
committerAlexander Romanov <a.romanov@blend4web.com>2017-04-13 12:33:05 +0300
commita7b3047cefcbfae4d8b13e15026497fd5ae92730 (patch)
treef026cb425cc41436047359d5e418042e65affa4b /source/blender/python
parentf90a243d9c0fc53081744f8685aa8fbaddc9810c (diff)
Datablock ID Properties
The absence of datablock properties "will certainly be resolved soon as the need for them is becoming obvious" said the [[http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.67/Python_Nodes|Python Nodes release notes]]. So this patch allows Python scripts to create ID Properties which reference datablocks. This functionality is implemented for `PointerProperty` and now such properties can be created with Python. In addition to the standard update callback, `PointerProperty` can have a `poll` callback (standard RNA) which is useful for search menus. For details see the test included in this patch. Original author: @artfunkel Alexander (Blend4Web Team) Reviewers: brecht, artfunkel, mont29, campbellbarton Reviewed By: mont29, campbellbarton Subscribers: jta, sergey, campbellbarton, wisaac, poseidon4o, mont29, homyachetser, Evgeny_Rodygin, AlexKowel, yurikovelenov, fjuhec, sharlybg, cardboard, duarteframos, blueprintrandom, a.romanov, BYOB, disnel, aditiapratama, bliblubli, dfelinto, lukastoenne Maniphest Tasks: T37754 Differential Revision: https://developer.blender.org/D113
Diffstat (limited to 'source/blender/python')
-rw-r--r--source/blender/python/generic/idprop_py_api.c21
-rw-r--r--source/blender/python/intern/bpy_props.c133
-rw-r--r--source/blender/python/intern/bpy_props.h4
-rw-r--r--source/blender/python/intern/bpy_rna.c44
-rw-r--r--source/blender/python/intern/bpy_rna.h1
5 files changed, 167 insertions, 36 deletions
diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c
index 0a9931f2683..5d6a7c578a2 100644
--- a/source/blender/python/generic/idprop_py_api.c
+++ b/source/blender/python/generic/idprop_py_api.c
@@ -43,6 +43,9 @@
#include "python_utildefines.h"
+extern bool pyrna_id_FromPyObject(PyObject *obj, ID **id);
+extern PyObject *pyrna_id_CreatePyObject(ID *id);
+extern bool pyrna_id_CheckPyObject(PyObject *obj);
/*********************** ID Property Main Wrapper Stuff ***************/
@@ -88,6 +91,11 @@ static PyObject *idprop_py_from_idp_group(ID *id, IDProperty *prop, IDProperty *
return (PyObject *)group;
}
+static PyObject *idprop_py_from_idp_id(IDProperty *prop)
+{
+ return pyrna_id_CreatePyObject(prop->data.pointer);
+}
+
static PyObject *idprop_py_from_idp_array(ID *id, IDProperty *prop)
{
BPy_IDProperty *array = PyObject_New(BPy_IDProperty, &BPy_IDArray_Type);
@@ -148,6 +156,7 @@ PyObject *BPy_IDGroup_WrapData(ID *id, IDProperty *prop, IDProperty *parent)
case IDP_GROUP: return idprop_py_from_idp_group(id, prop, parent);
case IDP_ARRAY: return idprop_py_from_idp_array(id, prop);
case IDP_IDPARRAY: return idprop_py_from_idp_idparray(id, prop); /* this could be better a internal type */
+ case IDP_ID: return idprop_py_from_idp_id(prop);
default: Py_RETURN_NONE;
}
}
@@ -586,8 +595,15 @@ static IDProperty *idp_from_PyMapping(const char *name, PyObject *ob)
return prop;
}
+static IDProperty *idp_from_DatablockPointer(const char *name, PyObject *ob, IDPropertyTemplate *val)
+{
+ pyrna_id_FromPyObject(ob, &val->id);
+ return IDP_New(IDP_ID, val, name);
+}
+
static IDProperty *idp_from_PyObject(PyObject *name_obj, PyObject *ob)
{
+ IDPropertyTemplate val = {0};
const char *name = idp_try_read_name(name_obj);
if (name == NULL) {
return NULL;
@@ -608,6 +624,9 @@ static IDProperty *idp_from_PyObject(PyObject *name_obj, PyObject *ob)
else if (PySequence_Check(ob)) {
return idp_from_PySequence(name, ob);
}
+ else if (ob == Py_None || pyrna_id_CheckPyObject(ob)) {
+ return idp_from_DatablockPointer(name, ob, &val);
+ }
else if (PyMapping_Check(ob)) {
return idp_from_PyMapping(name, ob);
}
@@ -732,6 +751,8 @@ static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop)
return idprop_py_from_idp_float(prop);
case IDP_DOUBLE:
return idprop_py_from_idp_double(prop);
+ case IDP_ID:
+ return idprop_py_from_idp_id(prop);
case IDP_ARRAY:
{
PyObject *seq = PyList_New(prop->len);
diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c
index f7348ac2250..2656e612b18 100644
--- a/source/blender/python/intern/bpy_props.c
+++ b/source/blender/python/intern/bpy_props.c
@@ -50,11 +50,13 @@
#include "../generic/py_capi_utils.h"
/* initial definition of callback slots we'll probably have more than 1 */
-#define BPY_DATA_CB_SLOT_SIZE 3
-
-#define BPY_DATA_CB_SLOT_UPDATE 0
-#define BPY_DATA_CB_SLOT_GET 1
-#define BPY_DATA_CB_SLOT_SET 2
+enum {
+ BPY_DATA_CB_SLOT_UPDATE = 0,
+ BPY_DATA_CB_SLOT_GET = 1,
+ BPY_DATA_CB_SLOT_SET = 2,
+ BPY_DATA_CB_SLOT_POLL = 3,
+ BPY_DATA_CB_SLOT_SIZE = 4,
+};
extern BPy_StructRNA *bpy_context_module;
@@ -71,6 +73,9 @@ static EnumPropertyItem property_flag_items[] = {
" :arg options: Enumerator in ['HIDDEN', 'SKIP_SAVE', 'ANIMATABLE', 'LIBRARY_EDITABLE', 'PROPORTIONAL'," \
"'TEXTEDIT_UPDATE'].\n" \
" :type options: set\n" \
+" :arg poll: function to be called to determine whether an item is valid for this property.\n" \
+" The function must take 2 values (self,object) and return Bool.\n" \
+" :type poll: function\n" \
static EnumPropertyItem property_flag_enum_items[] = {
{PROP_HIDDEN, "HIDDEN", 0, "Hidden", ""},
@@ -389,6 +394,51 @@ static void bpy_prop_boolean_set_cb(struct PointerRNA *ptr, struct PropertyRNA *
}
}
+static int bpy_prop_poll_cb(struct PointerRNA *self, PointerRNA candidate, struct PropertyRNA *prop)
+{
+ PyObject *py_self;
+ PyObject *py_candidate;
+ PyObject *py_func;
+ PyObject **py_data = RNA_property_py_data_get(prop);
+ PyObject *args;
+ PyObject *ret;
+ bool result;
+ const int is_write_ok = pyrna_write_check();
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ BLI_assert(self != NULL);
+
+ py_self = pyrna_struct_as_instance(self);
+ py_candidate = pyrna_struct_as_instance(&candidate);
+ py_func = py_data[BPY_DATA_CB_SLOT_POLL];
+
+ if (!is_write_ok)
+ pyrna_write_set(true);
+
+ args = PyTuple_New(2);
+ PyTuple_SET_ITEM(args, 0, py_self);
+ PyTuple_SET_ITEM(args, 1, py_candidate);
+
+ ret = PyObject_CallObject(py_func, args);
+
+ Py_DECREF(args);
+
+ if (ret == NULL) {
+ printf_func_error(py_func);
+ result = false;
+ }
+ else {
+ result = PyObject_IsTrue(ret);
+ Py_DECREF(ret);
+ }
+
+ PyGILState_Release(gilstate);
+ if (!is_write_ok)
+ pyrna_write_set(false);
+
+ return result;
+}
+
static void bpy_prop_boolean_array_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, int *values)
{
PyObject **py_data = RNA_property_py_data_get(prop);
@@ -1598,6 +1648,16 @@ static void bpy_prop_callback_assign_update(struct PropertyRNA *prop, PyObject *
}
}
+static void bpy_prop_callback_assign_pointer(struct PropertyRNA *prop, PyObject *poll_cb)
+{
+ if (poll_cb && poll_cb != Py_None) {
+ PyObject **py_data = bpy_prop_py_data_get(prop);
+
+ RNA_def_property_poll_runtime(prop, (void *) bpy_prop_poll_cb);
+ py_data[BPY_DATA_CB_SLOT_POLL] = poll_cb;
+ }
+}
+
static void bpy_prop_callback_assign_boolean(struct PropertyRNA *prop, PyObject *get_cb, PyObject *set_cb)
{
BooleanPropertyGetFunc rna_get_cb = NULL;
@@ -1904,7 +1964,7 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop, PyObject *ge
" :type set: function\n" \
#define BPY_PROPDEF_TYPE_DOC \
-" :arg type: A subclass of :class:`bpy.types.PropertyGroup`.\n" \
+" :arg type: A subclass of :class:`bpy.types.PropertyGroup` or :class:`bpy.types.ID`.\n" \
" :type type: class\n" \
#if 0
@@ -2772,7 +2832,7 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw)
Py_RETURN_NONE;
}
-static StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix)
+StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix)
{
StructRNA *srna;
@@ -2782,25 +2842,18 @@ static StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix
PyObject *msg = PyC_ExceptionBuffer();
const char *msg_char = _PyUnicode_AsString(msg);
PyErr_Format(PyExc_TypeError,
- "%.200s expected an RNA type derived from PropertyGroup, failed with: %s",
+ "%.200s expected an RNA type, failed with: %s",
error_prefix, msg_char);
Py_DECREF(msg);
}
else {
PyErr_Format(PyExc_TypeError,
- "%.200s expected an RNA type derived from PropertyGroup, failed with type '%s'",
+ "%.200s expected an RNA type, failed with type '%s'",
error_prefix, Py_TYPE(value)->tp_name);
}
return NULL;
}
- if (!RNA_struct_is_a(srna, &RNA_PropertyGroup)) {
- PyErr_Format(PyExc_TypeError,
- "%.200s expected an RNA type derived from PropertyGroup",
- error_prefix);
- return NULL;
- }
-
return srna;
}
@@ -2809,7 +2862,8 @@ PyDoc_STRVAR(BPy_PointerProperty_doc,
"name=\"\", "
"description=\"\", "
"options={'ANIMATABLE'}, "
- "update=None)\n"
+ "update=None,\n"
+ "poll=None)\n"
"\n"
" Returns a new pointer property definition.\n"
"\n"
@@ -2819,14 +2873,14 @@ BPY_PROPDEF_DESC_DOC
BPY_PROPDEF_OPTIONS_DOC
BPY_PROPDEF_UPDATE_DOC
);
-static PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw)
+PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
BPY_PROPDEF_HEAD(PointerProperty);
if (srna) {
- static const char *kwlist[] = {"attr", "type", "name", "description", "options", "update", NULL};
+ static const char *kwlist[] = {"attr", "type", "name", "description", "options", "poll", "update", NULL};
const char *id = NULL, *name = NULL, *description = "";
int id_len;
PropertyRNA *prop;
@@ -2834,33 +2888,47 @@ static PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *k
PyObject *type = Py_None;
PyObject *pyopts = NULL;
int opts = 0;
- PyObject *update_cb = NULL;
+ PyObject *update_cb = NULL, *poll_cb = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kw,
- "s#O|ssO!O:PointerProperty",
+ "s#O|ssO!OOO:PointerProperty",
(char **)kwlist, &id, &id_len,
&type, &name, &description,
&PySet_Type, &pyopts,
- &update_cb))
+ &poll_cb, &update_cb))
{
return NULL;
}
BPY_PROPDEF_CHECK(PointerProperty, property_flag_items);
- ptype = pointer_type_from_py(type, "PointerProperty(...):");
+ ptype = pointer_type_from_py(type, "PointerProperty(...)");
if (!ptype)
return NULL;
-
+ if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup) && !RNA_struct_is_ID(ptype)) {
+ PyErr_Format(PyExc_TypeError,
+ "PointerProperty(...) expected an RNA type derived from %.200s or %.200s",
+ RNA_struct_ui_name(&RNA_ID), RNA_struct_ui_name(&RNA_PropertyGroup));
+ return NULL;
+ }
if (bpy_prop_callback_check(update_cb, "update", 2) == -1) {
return NULL;
}
-
+ if (bpy_prop_callback_check(poll_cb, "poll", 2) == -1) {
+ return NULL;
+ }
prop = RNA_def_pointer_runtime(srna, id, ptype, name ? name : id, description);
if (pyopts) {
bpy_prop_assign_flag(prop, opts);
}
+
+ if (RNA_struct_idprops_contains_datablock(ptype)) {
+ if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) {
+ RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES);
+ }
+ }
bpy_prop_callback_assign_update(prop, update_cb);
+ bpy_prop_callback_assign_pointer(prop, poll_cb);
RNA_def_property_duplicate_pointers(srna, prop);
}
Py_RETURN_NONE;
@@ -2879,7 +2947,7 @@ BPY_PROPDEF_NAME_DOC
BPY_PROPDEF_DESC_DOC
BPY_PROPDEF_OPTIONS_DOC
);
-static PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw)
+PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
@@ -2910,10 +2978,23 @@ static PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject
if (!ptype)
return NULL;
+ if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup)) {
+ PyErr_Format(PyExc_TypeError,
+ "CollectionProperty(...) expected an RNA type derived from %.200s",
+ RNA_struct_ui_name(&RNA_ID), RNA_struct_ui_name(&RNA_PropertyGroup));
+ return NULL;
+ }
+
prop = RNA_def_collection_runtime(srna, id, ptype, name ? name : id, description);
if (pyopts) {
bpy_prop_assign_flag(prop, opts);
}
+
+ if (RNA_struct_idprops_contains_datablock(ptype)) {
+ if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) {
+ RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES);
+ }
+ }
RNA_def_property_duplicate_pointers(srna, prop);
}
Py_RETURN_NONE;
diff --git a/source/blender/python/intern/bpy_props.h b/source/blender/python/intern/bpy_props.h
index c9934ca0cf3..614c1b4b708 100644
--- a/source/blender/python/intern/bpy_props.h
+++ b/source/blender/python/intern/bpy_props.h
@@ -30,6 +30,10 @@
PyObject *BPY_rna_props(void);
+PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw);
+StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix);
+
#define PYRNA_STACK_ARRAY 32
#endif
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index 2fd46ab94f0..00627030db3 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -1934,16 +1934,10 @@ static int pyrna_py_to_prop(
}
else {
/* data == NULL, assign to RNA */
- if (value == Py_None) {
- PointerRNA valueptr = {{NULL}};
- RNA_property_pointer_set(ptr, prop, valueptr);
- }
- else if (RNA_struct_is_a(param->ptr.type, ptr_type)) {
- RNA_property_pointer_set(ptr, prop, param->ptr);
- }
- else {
+ if (value == Py_None || RNA_struct_is_a(param->ptr.type, ptr_type))
+ RNA_property_pointer_set(ptr, prop, value == Py_None ? PointerRNA_NULL : param->ptr);
+ else
raise_error = true;
- }
}
if (raise_error) {
@@ -3277,6 +3271,16 @@ static int pyrna_struct_ass_subscript(BPy_StructRNA *self, PyObject *key, PyObje
return -1;
}
+ BPy_StructRNA *val = (BPy_StructRNA *)value;
+ if (val && self->ptr.type && val->ptr.type) {
+ if (!RNA_struct_idprops_datablock_allowed(self->ptr.type) &&
+ RNA_struct_idprops_contains_datablock(val->ptr.type))
+ {
+ PyErr_SetString(PyExc_TypeError, "bpy_struct[key] = val: datablock id properties not supported for this type");
+ return -1;
+ }
+ }
+
return BPy_Wrap_SetMapItem(group, key, value);
}
@@ -6745,7 +6749,7 @@ PyObject *pyrna_id_CreatePyObject(ID *id)
bool pyrna_id_FromPyObject(PyObject *obj, ID **id)
{
- if (BPy_StructRNA_Check(obj) && (RNA_struct_is_ID(((BPy_StructRNA *)obj)->ptr.type))) {
+ if (pyrna_id_CheckPyObject(obj)) {
*id = ((BPy_StructRNA *)obj)->ptr.id.data;
return true;
}
@@ -6755,6 +6759,11 @@ bool pyrna_id_FromPyObject(PyObject *obj, ID **id)
}
}
+bool pyrna_id_CheckPyObject(PyObject *obj)
+{
+ return BPy_StructRNA_Check(obj) && (RNA_struct_is_ID(((BPy_StructRNA *) obj)->ptr.type));
+}
+
void BPY_rna_init(void)
{
#ifdef USE_MATHUTILS /* register mathutils callbacks, ok to run more than once. */
@@ -7089,6 +7098,21 @@ static int deferred_register_prop(StructRNA *srna, PyObject *key, PyObject *item
args_fake = PyTuple_New(1);
PyTuple_SET_ITEM(args_fake, 0, py_srna_cobject);
+ PyObject *type = PyDict_GetItemString(py_kw, "type");
+ StructRNA *type_srna = srna_from_self(type, "");
+ if (type_srna) {
+ if (!RNA_struct_idprops_datablock_allowed(srna) &&
+ (*(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(py_func) == BPy_PointerProperty ||
+ *(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(py_func) == BPy_CollectionProperty) &&
+ RNA_struct_idprops_contains_datablock(type_srna))
+ {
+ PyErr_Format(PyExc_ValueError,
+ "bpy_struct \"%.200s\" doesn't support datablock properties \n",
+ RNA_struct_identifier(srna));
+ return -1;
+ }
+ }
+
py_ret = PyObject_Call(py_func, args_fake, py_kw);
if (py_ret) {
diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h
index e38d4f095d6..605f79b1ad8 100644
--- a/source/blender/python/intern/bpy_rna.h
+++ b/source/blender/python/intern/bpy_rna.h
@@ -179,6 +179,7 @@ PyObject *pyrna_prop_CreatePyObject(PointerRNA *ptr, PropertyRNA *prop);
/* extern'd by other modules which don't deal closely with RNA */
PyObject *pyrna_id_CreatePyObject(struct ID *id);
bool pyrna_id_FromPyObject(PyObject *obj, struct ID **id);
+bool pyrna_id_CheckPyObject(PyObject *obj);
/* operators also need this to set args */
int pyrna_pydict_to_props(PointerRNA *ptr, PyObject *kw, const bool all_args, const char *error_prefix);