[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [33631] trunk/blender/source/blender: Expose access to PROP_ENUM_FLAG from bpy.props.EnumProperty(), this is exposed as a python set().

Campbell Barton ideasman42 at gmail.com
Mon Dec 13 10:10:16 CET 2010


Revision: 33631
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=33631
Author:   campbellbarton
Date:     2010-12-13 10:10:16 +0100 (Mon, 13 Dec 2010)

Log Message:
-----------
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.

Modified Paths:
--------------
    trunk/blender/source/blender/makesrna/intern/rna_access.c
    trunk/blender/source/blender/python/intern/bpy_props.c

Modified: trunk/blender/source/blender/makesrna/intern/rna_access.c
===================================================================
--- trunk/blender/source/blender/makesrna/intern/rna_access.c	2010-12-13 09:04:04 UTC (rev 33630)
+++ trunk/blender/source/blender/makesrna/intern/rna_access.c	2010-12-13 09:10:16 UTC (rev 33631)
@@ -3807,7 +3807,31 @@
 		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 {

Modified: trunk/blender/source/blender/python/intern/bpy_props.c
===================================================================
--- trunk/blender/source/blender/python/intern/bpy_props.c	2010-12-13 09:04:04 UTC (rev 33630)
+++ trunk/blender/source/blender/python/intern/bpy_props.c	2010-12-13 09:10:16 UTC (rev 33631)
@@ -40,6 +40,12 @@
 	{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 @@
 
 /* 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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 		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 @@
 	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;
+		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 && def_used == 0 && strcmp(def_cmp, tmp.identifier)==0) {
+				*defvalue= tmp.value;
+				def_used++; /* only ever 1 */
+			}
+		}
+
 		RNA_enum_item_add(&items, &totitem, &tmp);
 
-		if(def[0] && strcmp(def, tmp.identifier) == 0)
-			*defvalue= tmp.value;
-
 		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);
+

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list