summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog13
-rw-r--r--gobject/gobjectmodule.c13
-rw-r--r--gobject/pygobject-private.h3
-rw-r--r--gobject/pygobject.c345
-rw-r--r--tests/Makefile.am1
5 files changed, 374 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index b8d7dbd3..b6b1f17b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2005-07-08 Johan Dahlin <jdahlin@async.com.br>
+
+ * gobject/gobjectmodule.c: (initgobject):
+ * gobject/pygobject-private.h:
+ * gobject/pygobject.c: (pyg_props_iter_dealloc),
+ (pygobject_props_iter_next), (PyGProps_dealloc),
+ (build_parameter_list), (PyGProps_getattro), (PyGProps_setattro),
+ (pygobject_props_traverse), (pygobject_props_get_iter),
+ (PyGProps_length), (pyg_props_descr_descr_get):
+ * tests/Makefile.am:
+ Add GObject support, patch mostly based on Iņaki and Gustavos work.
+ Unittests added, fixes #81879
+
2005-07-07 Johan Dahlin <jdahlin@async.com.br>
* gobject/gobjectmodule.c: (initgobject):
diff --git a/gobject/gobjectmodule.c b/gobject/gobjectmodule.c
index ad008459..e1513069 100644
--- a/gobject/gobjectmodule.c
+++ b/gobject/gobjectmodule.c
@@ -2542,6 +2542,7 @@ DL_EXPORT(void)
initgobject(void)
{
PyObject *m, *d, *o, *tuple;
+ PyObject *descr;
PyGParamSpec_Type.ob_type = &PyType_Type;
m = Py_InitModule("gobject", pygobject_functions);
@@ -2574,7 +2575,7 @@ initgobject(void)
PyGObject_MetaType.tp_is_gc = PyType_Type.tp_is_gc;
PyType_Ready(&PyGObject_MetaType);
PyDict_SetItemString(d, "GObjectMeta", (PyObject *) &PyGObject_MetaType);
-
+
PyGObject_Type.tp_alloc = PyType_GenericAlloc;
PyGObject_Type.tp_new = PyType_GenericNew;
pygobject_register_class(d, "GObject", G_TYPE_OBJECT,
@@ -2582,6 +2583,16 @@ initgobject(void)
PyDict_SetItemString(PyGObject_Type.tp_dict, "__gdoc__",
pyg_object_descr_doc_get());
+ /* GObject properties descriptor */
+ if (PyType_Ready(&PyGProps_Type) < 0)
+ return;
+ if (PyType_Ready(&PyGPropsDescr_Type) < 0)
+ return;
+ if (PyType_Ready(&PyGPropsIter_Type) < 0)
+ return;
+ descr = PyObject_New(PyObject, &PyGPropsDescr_Type);
+ PyDict_SetItemString(PyGObject_Type.tp_dict, "props", descr);
+
REGISTER_GTYPE(d, PyGInterface_Type, "GInterface", G_TYPE_INTERFACE);
PyDict_SetItemString(PyGInterface_Type.tp_dict, "__doc__",
pyg_object_descr_doc_get());
diff --git a/gobject/pygobject-private.h b/gobject/pygobject-private.h
index 205d2557..4fd48e40 100644
--- a/gobject/pygobject-private.h
+++ b/gobject/pygobject-private.h
@@ -93,6 +93,9 @@ extern PyTypeObject PyGObject_MetaType;
/* from pygobject.h */
extern PyTypeObject PyGObject_Type;
extern PyTypeObject PyGInterface_Type;
+extern PyTypeObject PyGProps_Type;
+extern PyTypeObject PyGPropsDescr_Type;
+extern PyTypeObject PyGPropsIter_Type;
void pygobject_register_class (PyObject *dict,
const gchar *type_name,
diff --git a/gobject/pygobject.c b/gobject/pygobject.c
index 2b2b2c4b..228769bc 100644
--- a/gobject/pygobject.c
+++ b/gobject/pygobject.c
@@ -80,6 +80,351 @@ pygobject_register_sinkfunc(GType type, void (* sinkfunc)(GObject *object))
g_array_append_val(sink_funcs, sf);
}
+typedef struct {
+ PyObject_HEAD
+ GParamSpec **props;
+ guint n_props;
+ guint index;
+} PyGPropsIter;
+
+static void
+pyg_props_iter_dealloc(PyGPropsIter *self)
+{
+ g_free(self->props);
+ PyObject_Del((PyObject*) self);
+}
+
+static PyObject*
+pygobject_props_iter_next(PyGPropsIter *iter)
+{
+ if (iter->index < iter->n_props)
+ return pyg_param_spec_new(iter->props[iter->index++]);
+ else {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+}
+
+
+PyTypeObject PyGPropsIter_Type = {
+ PyObject_HEAD_INIT(NULL)
+ sizeof(PyObject), /* ob_size */
+ "gobject.GPropsIter", /* tp_name */
+ 0, /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)pyg_props_iter_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ "GObject properties iterator", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ (iternextfunc)pygobject_props_iter_next, /* tp_iternext */
+};
+
+typedef struct {
+ PyObject_HEAD
+ /* a reference to the object containing the properties */
+ PyGObject *pygobject;
+ GType gtype;
+} PyGProps;
+
+static void
+PyGProps_dealloc(PyGProps* self)
+{
+ PyGObject *tmp;
+
+ PyObject_GC_UnTrack((PyObject*)self);
+
+ tmp = self->pygobject;
+ self->pygobject = NULL;
+ Py_XDECREF(tmp);
+
+ PyObject_GC_Del((PyObject*)self);
+}
+
+static PyObject*
+build_parameter_list(GObjectClass *class)
+{
+ GParamSpec **props;
+ guint n_props = 0, i;
+ PyObject *prop_str;
+ char *name;
+ PyObject *props_list;
+
+ props = g_object_class_list_properties(class, &n_props);
+ props_list = PyList_New(n_props);
+ for (i = 0; i < n_props; i++) {
+ name = g_strdup(g_param_spec_get_name(props[i]));
+ /* hyphens cannot belong in identifiers */
+ g_strdelimit(name, "-", '_');
+ prop_str = PyString_FromString(name);
+
+ PyList_SetItem(props_list, i, prop_str);
+ }
+
+ if (props)
+ g_free(props);
+
+ return props_list;
+}
+
+static PyObject*
+PyGProps_getattro(PyGProps *self, PyObject *attr)
+{
+ char *attr_name;
+ GObjectClass *class;
+ GParamSpec *pspec;
+ GValue value = { 0, };
+ PyObject *ret;
+
+ attr_name = PyString_AsString(attr);
+ if (!attr_name) {
+ PyErr_Clear();
+ return PyObject_GenericGetAttr((PyObject *)self, attr);
+ }
+
+ class = g_type_class_ref(self->gtype);
+
+ if (!strcmp(attr_name, "__members__")) {
+ return build_parameter_list(class);
+ }
+
+ pspec = g_object_class_find_property(class, attr_name);
+ g_type_class_unref(class);
+
+ if (!pspec) {
+ return PyObject_GenericGetAttr((PyObject *)self, attr);
+ }
+
+ if (!(pspec->flags & G_PARAM_READABLE)) {
+ PyErr_Format(PyExc_TypeError,
+ "property '%s' is not readable", attr_name);
+ return NULL;
+ }
+
+ /* If we're doing it without an instance, return a GParamSpec */
+ if (!self->pygobject) {
+ return pyg_param_spec_new(pspec);
+ }
+
+ g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
+ g_object_get_property(self->pygobject->obj, attr_name, &value);
+ ret = pyg_param_gvalue_as_pyobject(&value, TRUE, pspec);
+ g_value_unset(&value);
+
+ return ret;
+}
+
+static int
+PyGProps_setattro(PyGProps *self, PyObject *attr, PyObject *pvalue)
+{
+ GParamSpec *pspec;
+ GValue value = { 0, };
+ char *attr_name;
+ GObject *obj;
+
+ if (pvalue == NULL) {
+ PyErr_SetString(PyExc_TypeError, "properties cannot be "
+ "deleted");
+ return -1;
+ }
+
+ attr_name = PyString_AsString(attr);
+ if (!attr_name) {
+ PyErr_Clear();
+ return PyObject_GenericSetAttr((PyObject *)self, attr, pvalue);
+ }
+
+ if (!self->pygobject) {
+ PyErr_SetString(PyExc_TypeError,
+ "cannot set GOject properties without an instance");
+ return -1;
+ }
+
+ obj = self->pygobject->obj;
+ pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(obj), attr_name);
+ if (!pspec) {
+ return PyObject_GenericSetAttr((PyObject *)self, attr, pvalue);
+ }
+
+ if (!(pspec->flags & G_PARAM_WRITABLE)) {
+ PyErr_Format(PyExc_TypeError,
+ "property '%s' is not writable", attr_name);
+ return -1;
+ }
+
+ g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
+ if (pyg_param_gvalue_from_pyobject(&value, pvalue, pspec) < 0) {
+ PyErr_SetString(PyExc_TypeError,
+ "could not convert argument to correct param type");
+ return -1;
+ }
+
+ g_object_set_property(obj, attr_name, &value);
+ g_value_unset(&value);
+ return 0;
+}
+
+static int
+pygobject_props_traverse(PyGProps *self, visitproc visit, void *arg)
+{
+ if (self->pygobject && visit((PyObject *) self->pygobject, arg) < 0)
+ return -1;
+ return 0;
+}
+
+static PyObject*
+pygobject_props_get_iter(PyGProps *self)
+{
+ PyGPropsIter *iter;
+ GObjectClass *class;
+
+ iter = PyObject_NEW(PyGPropsIter, &PyGPropsIter_Type);
+ class = g_type_class_ref(self->gtype);
+ iter->props = g_object_class_list_properties(class, &iter->n_props);
+ iter->index = 0;
+ g_type_class_unref(class);
+ return (PyObject *) iter;
+}
+
+static int
+PyGProps_length(PyGProps *self)
+{
+ GObjectClass *class;
+ int n_props;
+
+ class = g_type_class_ref(self->gtype);
+ g_object_class_list_properties(class, &n_props);
+ g_type_class_unref(class);
+
+ return n_props;
+}
+
+static PySequenceMethods _PyGProps_as_sequence = {
+ (inquiry)PyGProps_length,
+ (binaryfunc)0,
+ (intargfunc)0,
+ (intargfunc)0,
+ (intintargfunc)0,
+ (intobjargproc)0,
+ (intintobjargproc)0
+};
+
+PyTypeObject PyGProps_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gobject.GProps", /* tp_name */
+ sizeof(PyGProps), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)PyGProps_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ (PySequenceMethods*)&_PyGProps_as_sequence, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ (getattrofunc)PyGProps_getattro, /* tp_getattro */
+ (setattrofunc)PyGProps_setattro, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC, /* tp_flags */
+ "The properties of the GObject accessible as "
+ "Python attributes.", /* tp_doc */
+ (traverseproc)pygobject_props_traverse, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)pygobject_props_get_iter, /* tp_iter */
+ 0, /* tp_iternext */
+};
+
+
+static PyObject *
+pyg_props_descr_descr_get(PyObject *self, PyObject *obj, PyObject *type)
+{
+ PyGProps *gprops;
+
+ gprops = PyObject_GC_New(PyGProps, &PyGProps_Type);
+ if (obj == NULL || obj == Py_None) {
+ gprops->pygobject = NULL;
+ gprops->gtype = pyg_type_from_object(type);
+ } else {
+ if (!PyObject_IsInstance(obj, (PyObject *) &PyGObject_Type)) {
+ PyErr_SetString(PyExc_TypeError, "cannot use GObject property"
+ " descriptor on non-GObject instances");
+ return NULL;
+ }
+ Py_INCREF(obj);
+ gprops->pygobject = (PyGObject *) obj;
+ gprops->gtype = pyg_type_from_object(obj);
+ }
+ return (PyObject *) gprops;
+}
+
+PyTypeObject PyGPropsDescr_Type = {
+ PyObject_HEAD_INIT(NULL)
+ sizeof(PyObject), /* ob_size */
+ "gobject.GPropsDescr", /* tp_name */
+ 0, /* tp_basicsize */
+ 0, /* tp_itemsize */
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ 0, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ pyg_props_descr_descr_get, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+};
+
+
/**
* pygobject_register_class:
* @dict: the module dictionary. A reference to the type will be stored here.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 9b0b8de0..0f8ac7b8 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -25,6 +25,7 @@ tests = \
test_gtype.py \
test_mainloop.py \
test_radiobutton.py \
+ test_properties.py \
test_signal.py \
test_subprocess.py \
test_subtype.py \