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.
This commit is contained in:
Campbell Barton 2010-12-13 09:10:16 +00:00
parent 25bf9e38f3
commit a4b410af3d
2 changed files with 118 additions and 31 deletions

@ -3807,7 +3807,31 @@ char *RNA_property_as_string(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
const char *identifier;
int val = RNA_property_enum_get(ptr, prop);
if(RNA_property_enum_identifier(C, ptr, prop, val, &identifier)) {
if(RNA_property_flag(prop) & PROP_ENUM_FLAG) {
/* represent as a python set */
EnumPropertyItem *item= NULL;
int free;
BLI_dynstr_append(dynstr, "{");
RNA_property_enum_items(C, ptr, prop, &item, NULL, &free);
if(item) {
short is_first= TRUE;
for (; item->identifier; item++) {
if(item->identifier[0] && item->value & val) {
BLI_dynstr_appendf(dynstr, is_first ? "'%s'" : ", '%s'", item->identifier);
is_first= FALSE;
}
}
if(free) {
MEM_freeN(item);
}
}
BLI_dynstr_append(dynstr, "}");
}
else if(RNA_property_enum_identifier(C, ptr, prop, val, &identifier)) {
BLI_dynstr_appendf(dynstr, "'%s'", identifier);
}
else {

@ -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);
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[0] && strcmp(def, tmp.identifier) == 0)
if(def && PySet_Contains(def, PyTuple_GET_ITEM(item, 0))) {
*defvalue |= tmp.value;
def_used++;
}
}
else {
tmp.value= i;
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)