diff options
author | Campbell Barton <ideasman42@gmail.com> | 2010-12-13 12:10:16 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2010-12-13 12:10:16 +0300 |
commit | a4b410af3d67c44a35c9cbf6f09c33a3c41a50af (patch) | |
tree | 8eccf50b13bdfb6cd63bd953965d4555a6154caf /source/blender/python | |
parent | 25bf9e38f3fbeb1e36f29b1296f0bbc2dd61f9d2 (diff) |
Expose access to PROP_ENUM_FLAG from bpy.props.EnumProperty(), this is exposed as a python set(). The default value is also a python set() so multiple booleans can be used in the 1 property.
Also added PROP_ENUM_FLAG support to operator printouts.
Diffstat (limited to 'source/blender/python')
-rw-r--r-- | source/blender/python/intern/bpy_props.c | 123 |
1 files changed, 93 insertions, 30 deletions
diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index b7192f7395b..1148b9e5e2b 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -40,6 +40,12 @@ EnumPropertyItem property_flag_items[] = { {PROP_ANIMATABLE, "ANIMATABLE", 0, "Animateable", ""}, {0, NULL, 0, NULL, NULL}}; +EnumPropertyItem property_flag_enum_items[] = { + {PROP_HIDDEN, "HIDDEN", 0, "Hidden", ""}, + {PROP_ANIMATABLE, "ANIMATABLE", 0, "Animateable", ""}, + {PROP_ENUM_FLAG, "ENUM_FLAG", 0, "Enum Flag", ""}, + {0, NULL, 0, NULL, NULL}}; + /* subtypes */ EnumPropertyItem property_subtype_string_items[] = { {PROP_FILEPATH, "FILE_PATH", 0, "File Path", ""}, @@ -134,7 +140,7 @@ static PyObject *bpy_prop_deferred_return(PyObject *func, PyObject *kw) /* terse macros for error checks shared between all funcs cant use function * calls because of static strins passed to pyrna_set_to_enum_bitfield */ -#define BPY_PROPDEF_CHECK(_func) \ +#define BPY_PROPDEF_CHECK(_func, _property_flag_items) \ if(id_len >= MAX_IDPROP_NAME) { \ PyErr_Format(PyExc_TypeError, #_func"(): '%.200s' too long, max length is %d", id, MAX_IDPROP_NAME-1); \ return NULL; \ @@ -143,11 +149,11 @@ static PyObject *bpy_prop_deferred_return(PyObject *func, PyObject *kw) PyErr_Format(PyExc_TypeError, #_func"(): '%s' is defined as a non-dynamic type", id); \ return NULL; \ } \ - if(pyopts && pyrna_set_to_enum_bitfield(property_flag_items, pyopts, &opts, #_func"(options={...}):")) \ + if(pyopts && pyrna_set_to_enum_bitfield(_property_flag_items, pyopts, &opts, #_func"(options={...}):")) \ return NULL; \ -#define BPY_PROPDEF_SUBTYPE_CHECK(_func, _subtype) \ - BPY_PROPDEF_CHECK(_func) \ +#define BPY_PROPDEF_SUBTYPE_CHECK(_func, _property_flag_items, _subtype) \ + BPY_PROPDEF_CHECK(_func, _property_flag_items) \ if(pysubtype && RNA_enum_value_from_id(_subtype, pysubtype, &subtype)==0) { \ PyErr_Format(PyExc_TypeError, #_func"(subtype='%s'): invalid subtype", pysubtype); \ return NULL; \ @@ -196,7 +202,7 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw) if (!PyArg_ParseTupleAndKeywords(args, kw, "s#|ssiO!s:BoolProperty", (char **)kwlist, &id, &id_len, &name, &description, &def, &PySet_Type, &pyopts, &pysubtype)) return NULL; - BPY_PROPDEF_SUBTYPE_CHECK(BoolProperty, property_subtype_number_items) + BPY_PROPDEF_SUBTYPE_CHECK(BoolProperty, property_flag_items, property_subtype_number_items) prop= RNA_def_property(srna, id, PROP_BOOLEAN, subtype); RNA_def_property_boolean_default(prop, def); @@ -243,7 +249,7 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject if (!PyArg_ParseTupleAndKeywords(args, kw, "s#|ssOO!si:BoolVectorProperty", (char **)kwlist, &id, &id_len, &name, &description, &pydef, &PySet_Type, &pyopts, &pysubtype, &size)) return NULL; - BPY_PROPDEF_SUBTYPE_CHECK(BoolVectorProperty, property_subtype_array_items) + BPY_PROPDEF_SUBTYPE_CHECK(BoolVectorProperty, property_flag_items, property_subtype_array_items) if(size < 1 || size > PYRNA_STACK_ARRAY) { PyErr_Format(PyExc_TypeError, "BoolVectorProperty(size=%d): size must be between 0 and " STRINGIFY(PYRNA_STACK_ARRAY), size); @@ -298,7 +304,7 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw) if (!PyArg_ParseTupleAndKeywords(args, kw, "s#|ssiiiiiiO!s:IntProperty", (char **)kwlist, &id, &id_len, &name, &description, &def, &min, &max, &soft_min, &soft_max, &step, &PySet_Type, &pyopts, &pysubtype)) return NULL; - BPY_PROPDEF_SUBTYPE_CHECK(IntProperty, property_subtype_number_items) + BPY_PROPDEF_SUBTYPE_CHECK(IntProperty, property_flag_items, property_subtype_number_items) prop= RNA_def_property(srna, id, PROP_INT, subtype); RNA_def_property_int_default(prop, def); @@ -346,7 +352,7 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject if (!PyArg_ParseTupleAndKeywords(args, kw, "s#|ssOiiiiiO!si:IntVectorProperty", (char **)kwlist, &id, &id_len, &name, &description, &pydef, &min, &max, &soft_min, &soft_max, &step, &PySet_Type, &pyopts, &pysubtype, &size)) return NULL; - BPY_PROPDEF_SUBTYPE_CHECK(IntVectorProperty, property_subtype_array_items) + BPY_PROPDEF_SUBTYPE_CHECK(IntVectorProperty, property_flag_items, property_subtype_array_items) if(size < 1 || size > PYRNA_STACK_ARRAY) { PyErr_Format(PyExc_TypeError, "IntVectorProperty(size=%d): size must be between 0 and " STRINGIFY(PYRNA_STACK_ARRAY), size); @@ -407,7 +413,7 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw) if (!PyArg_ParseTupleAndKeywords(args, kw, "s#|ssffffffiO!ss:FloatProperty", (char **)kwlist, &id, &id_len, &name, &description, &def, &min, &max, &soft_min, &soft_max, &step, &precision, &PySet_Type, &pyopts, &pysubtype, &pyunit)) return NULL; - BPY_PROPDEF_SUBTYPE_CHECK(FloatProperty, property_subtype_number_items) + BPY_PROPDEF_SUBTYPE_CHECK(FloatProperty, property_flag_items, property_subtype_number_items) if(pyunit && RNA_enum_value_from_id(property_unit_items, pyunit, &unit)==0) { PyErr_Format(PyExc_TypeError, "FloatProperty(unit='%s'): invalid unit"); @@ -460,7 +466,7 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec if (!PyArg_ParseTupleAndKeywords(args, kw, "s#|ssOfffffiO!si:FloatVectorProperty", (char **)kwlist, &id, &id_len, &name, &description, &pydef, &min, &max, &soft_min, &soft_max, &step, &precision, &PySet_Type, &pyopts, &pysubtype, &size)) return NULL; - BPY_PROPDEF_SUBTYPE_CHECK(FloatVectorProperty, property_subtype_array_items) + BPY_PROPDEF_SUBTYPE_CHECK(FloatVectorProperty, property_flag_items, property_subtype_array_items) if(size < 1 || size > PYRNA_STACK_ARRAY) { PyErr_Format(PyExc_TypeError, "FloatVectorProperty(size=%d): size must be between 0 and " STRINGIFY(PYRNA_STACK_ARRAY), size); @@ -515,7 +521,7 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw if (!PyArg_ParseTupleAndKeywords(args, kw, "s#|sssiO!s:StringProperty", (char **)kwlist, &id, &id_len, &name, &description, &def, &maxlen, &PySet_Type, &pyopts, &pysubtype)) return NULL; - BPY_PROPDEF_SUBTYPE_CHECK(StringProperty, property_subtype_string_items) + BPY_PROPDEF_SUBTYPE_CHECK(StringProperty, property_flag_items, property_subtype_string_items) prop= RNA_def_property(srna, id, PROP_STRING, subtype); if(maxlen != 0) RNA_def_property_string_maxlength(prop, maxlen + 1); /* +1 since it includes null terminator */ @@ -531,49 +537,102 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw Py_RETURN_NONE; } -static EnumPropertyItem *enum_items_from_py(PyObject *value, const char *def, int *defvalue) +static EnumPropertyItem *enum_items_from_py(PyObject *value, PyObject *def, int *defvalue, const short is_enum_flag) { EnumPropertyItem *items= NULL; PyObject *item; int seq_len, i, totitem= 0; + short def_used= 0; + const char *def_cmp= NULL; if(!PySequence_Check(value)) { - PyErr_SetString(PyExc_TypeError, "expected a sequence of tuples for the enum items"); + PyErr_SetString(PyExc_TypeError, "EnumProperty(...): expected a sequence of tuples for the enum items"); return NULL; } - seq_len = PySequence_Length(value); + seq_len= PySequence_Length(value); + + if(is_enum_flag) { + if(seq_len > RNA_ENUM_BITFLAG_SIZE) { + PyErr_SetString(PyExc_TypeError, "EnumProperty(...): maximum " STRINGIFY(RNA_ENUM_BITFLAG_SIZE) " members for a ENUM_FLAG type property"); + return NULL; + } + if(def && !PySet_Check(def)) { + PyErr_Format(PyExc_TypeError, "EnumProperty(...): default option must be a 'set' type when ENUM_FLAG is enabled, not a '%.200s'", Py_TYPE(def)->tp_name); + return NULL; + } + } + else { + if(def) { + def_cmp= _PyUnicode_AsString(def); + if(def_cmp==NULL) { + PyErr_Format(PyExc_TypeError, "EnumProperty(...): default option must be a 'str' type when ENUM_FLAG is disabled, not a '%.200s'", Py_TYPE(def)->tp_name); + return NULL; + } + } + } + + /* blank value */ + *defvalue= 0; + for(i=0; i<seq_len; i++) { EnumPropertyItem tmp= {0, "", 0, "", ""}; item= PySequence_GetItem(value, i); if(item==NULL || PyTuple_Check(item)==0) { - PyErr_SetString(PyExc_TypeError, "expected a sequence of tuples for the enum items"); + PyErr_SetString(PyExc_TypeError, "EnumProperty(...): expected a sequence of tuples for the enum items"); if(items) MEM_freeN(items); Py_XDECREF(item); return NULL; } if(!PyArg_ParseTuple(item, "sss", &tmp.identifier, &tmp.name, &tmp.description)) { - PyErr_SetString(PyExc_TypeError, "expected an identifier, name and description in the tuple"); + PyErr_SetString(PyExc_TypeError, "EnumProperty(...): expected an identifier, name and description in the tuple"); Py_DECREF(item); return NULL; } - tmp.value= i; - RNA_enum_item_add(&items, &totitem, &tmp); + if(is_enum_flag) { + tmp.value= 1<<i; + + if(def && PySet_Contains(def, PyTuple_GET_ITEM(item, 0))) { + *defvalue |= tmp.value; + def_used++; + } + } + else { + tmp.value= i; - if(def[0] && strcmp(def, tmp.identifier) == 0) - *defvalue= tmp.value; + if(def && def_used == 0 && strcmp(def_cmp, tmp.identifier)==0) { + *defvalue= tmp.value; + def_used++; /* only ever 1 */ + } + } + + RNA_enum_item_add(&items, &totitem, &tmp); Py_DECREF(item); } - if(!def[0]) - *defvalue= 0; - RNA_enum_item_end(&items, &totitem); + if(is_enum_flag) { + /* strict check that all set members were used */ + if(def && def_used != PySet_GET_SIZE(def)) { + MEM_freeN(items); + + PyErr_Format(PyExc_TypeError, "EnumProperty(..., default={...}): set has %d unused member(s)", PySet_GET_SIZE(def) - def_used); + return NULL; + } + } + else { + if(def && def_used == 0) { + MEM_freeN(items); + + PyErr_Format(PyExc_TypeError, "EnumProperty(..., default=\'%s\'): not found in enum members", def); + return NULL; + } + } return items; } @@ -582,7 +641,9 @@ static char BPy_EnumProperty_doc[] = "\n" " Returns a new enumerator property definition.\n" "\n" -" :arg options: Enumerator in ['HIDDEN', 'ANIMATABLE'].\n" +" :arg default: The default value for this enum, A string when *ENUM_FLAG* is disabled otherwise a set which may only contain string identifiers used in *items*.\n" +" :type default: string or set\n" +" :arg options: Enumerator in ['HIDDEN', 'ANIMATABLE', 'ENUM_FLAG'].\n" " :type options: set\n" " :arg items: The items that make up this enumerator.\n" " :type items: sequence of string triplets"; @@ -594,7 +655,8 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw) if(srna) { static const char *kwlist[] = {"attr", "items", "name", "description", "default", "options", NULL}; - const char *id=NULL, *name="", *description="", *def=""; + const char *id=NULL, *name="", *description=""; + PyObject *def= NULL; int id_len; int defvalue=0; PyObject *items= Py_None; @@ -603,12 +665,12 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw) PyObject *pyopts= NULL; int opts=0; - if (!PyArg_ParseTupleAndKeywords(args, kw, "s#O|sssO!:EnumProperty", (char **)kwlist, &id, &id_len, &items, &name, &description, &def, &PySet_Type, &pyopts)) + if (!PyArg_ParseTupleAndKeywords(args, kw, "s#O|ssOO!:EnumProperty", (char **)kwlist, &id, &id_len, &items, &name, &description, &def, &PySet_Type, &pyopts)) return NULL; - BPY_PROPDEF_CHECK(EnumProperty) + BPY_PROPDEF_CHECK(EnumProperty, property_flag_enum_items) - eitems= enum_items_from_py(items, def, &defvalue); + eitems= enum_items_from_py(items, def, &defvalue, (opts & PROP_ENUM_FLAG)!=0); if(!eitems) return NULL; @@ -616,6 +678,7 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw) if(pyopts) { if(opts & PROP_HIDDEN) RNA_def_property_flag(prop, PROP_HIDDEN); if((opts & PROP_ANIMATABLE)==0) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + if(opts & PROP_ENUM_FLAG) RNA_def_property_flag(prop, PROP_ENUM_FLAG); } RNA_def_property_duplicate_pointers(srna, prop); MEM_freeN(eitems); @@ -673,7 +736,7 @@ static PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *k if (!PyArg_ParseTupleAndKeywords(args, kw, "s#O|ssO!:PointerProperty", (char **)kwlist, &id, &id_len, &type, &name, &description, &PySet_Type, &pyopts)) return NULL; - BPY_PROPDEF_CHECK(PointerProperty) + BPY_PROPDEF_CHECK(PointerProperty, property_flag_items) ptype= pointer_type_from_py(type, "PointerProperty(...):"); if(!ptype) @@ -717,7 +780,7 @@ static PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject if (!PyArg_ParseTupleAndKeywords(args, kw, "s#O|ssO!:CollectionProperty", (char **)kwlist, &id, &id_len, &type, &name, &description, &PySet_Type, &pyopts)) return NULL; - BPY_PROPDEF_CHECK(CollectionProperty) + BPY_PROPDEF_CHECK(CollectionProperty, property_flag_items) ptype= pointer_type_from_py(type, "CollectionProperty(...):"); if(!ptype) |