summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Pogonyshev <pogonyshev@gmx.net>2009-05-12 00:47:51 +0300
committerPaul Pogonyshev <pogonyshev@gmx.net>2009-05-15 22:49:56 +0300
commit15b7265a00d405d6115f45ba0b1f26019ef1a52b (patch)
tree647ba8393bd2279b63099171398a5d45a2e9bc51
parent029b2905baec78b370e76e1b2340dc2ee2ddaea1 (diff)
downloadpygtk-15b7265a00d405d6115f45ba0b1f26019ef1a52b.tar.gz
Add floating-point support to gtk.gdk.Color
Make constructor accept floating-point arguments. Add 'red_float', 'green_float' and 'blue_float' read-write properties. Test and document new features. Part of bug #546019.
-rw-r--r--docs/reference/pygtk-gdkcolor.xml50
-rw-r--r--gtk/gdk-base-types.defs5
-rw-r--r--gtk/gdkcolor.override220
-rw-r--r--tests/test_color.py21
4 files changed, 260 insertions, 36 deletions
diff --git a/docs/reference/pygtk-gdkcolor.xml b/docs/reference/pygtk-gdkcolor.xml
index 60d3ab53..82bd82b0 100644
--- a/docs/reference/pygtk-gdkcolor.xml
+++ b/docs/reference/pygtk-gdkcolor.xml
@@ -51,6 +51,10 @@ linkend="constructor-gdkcolor">gtk.gdk.Color</link></methodname>
<refsect1>
<title>Attributes</title>
+ <note>
+ <para>Floating-point attributes are available in PyGTK 2.16 and above.</para>
+ </note>
+
<blockquote role="properties">
<informaltable pgwide="1" frame="none">
<tgroup cols="3">
@@ -84,11 +88,31 @@ linkend="constructor-gdkcolor">gtk.gdk.Color</link></methodname>
<entry>The value of the blue component of the color</entry>
</row>
+ <row valign="top">
+ <entry>"red_float"</entry>
+ <entry>Read-Write</entry>
+ <entry>The value of the red component of the color as a float in the range 0.0--1.0</entry>
+ </row>
+
+ <row valign="top">
+ <entry>"green_float"</entry>
+ <entry>Read-Write</entry>
+ <entry>The value of the green component of the color as a float in the range 0.0--1.0</entry>
+ </row>
+
+ <row valign="top">
+ <entry>"blue_float"</entry>
+ <entry>Read-Write</entry>
+ <entry>The value of the blue component of the color as a float in the range 0.0--1.0</entry>
+ </row>
+
</tbody>
</tgroup>
</informaltable>
</blockquote>
+ <para>For details on how assignment to <literal>*_float</literal> attributes work,
+ see <link linkend="constructor-gdkcolor">constructor documentation</link>.</para>
</refsect1>
<refsect1>
@@ -189,6 +213,12 @@ object</simpara></listitem>
<para>Second form of the constructor is available in PyGTK 2.14 and above.</para>
</note>
+ <note>
+ <para><parameter>red</parameter>, <parameter>green</parameter>
+ and <parameter>blue</parameter> can be floating-point numbers since PyGTK
+ 2.16.</para>
+ </note>
+
<para>Creates a new <link
linkend="class-gdkcolor"><classname>gtk.gdk.Color</classname></link> object
with the color component values specified by <parameter>red</parameter>,
@@ -200,6 +230,26 @@ allocated.</para>
<para>Second form of the constructor is analogous to
<link linkend="function-gdk--color-parse"><function>gtk.gdk.color_parse</function></link>.</para>
+ <para>Starting with PyGTK 2.16, <parameter>red</parameter>, <parameter>green</parameter> and
+ <parameter>blue</parameter> parameters can also be floating-point numbers in the
+ range 0.0--1.0. Either all specified values must be integers or floats -- mixing is
+ not allowed as this would be too error-prone. Values outside the valid range
+ 0.0--1.0 are clamped, so e.g. 3.14 is the same as 1.0.</para>
+
+ <para>Note that internally values are still stored as integers, so values of
+ corresponding <literal>*_float</literal> attribute will not necessarily be the same
+ as the value used as argument for the constructor. They will be as close as
+ permitted by 16-bit color component storage used by <classname>GdkColor</classname>
+ though.</para>
+
+ <para>All of the following expressions create a bright green color:</para>
+
+ <programlisting>
+ gtk.gdk.Color(0, 65535, 0)
+ gtk.gdk.Color(green=1.0)
+ gtk.gdk.Color('#0f0')
+ </programlisting>
+
</refsect1>
<refsect1>
diff --git a/gtk/gdk-base-types.defs b/gtk/gdk-base-types.defs
index 89d0c946..f6799bf2 100644
--- a/gtk/gdk-base-types.defs
+++ b/gtk/gdk-base-types.defs
@@ -228,6 +228,11 @@
'("guint16" "red")
'("guint16" "green")
'("guint16" "blue")
+ ;; Fake. Only defined since codegen currently doesn't support
+ ;; defining attributes in '.override' files.
+ '("gfloat" "red_float")
+ '("gfloat" "green_float")
+ '("gfloat" "blue_float")
)
)
diff --git a/gtk/gdkcolor.override b/gtk/gdkcolor.override
index 3cb5329a..f46b8783 100644
--- a/gtk/gdkcolor.override
+++ b/gtk/gdkcolor.override
@@ -32,25 +32,19 @@ _wrap_gdk_color_new(PyGBoxed *self,
{
static char *kwlist1[] = {"red", "green", "blue", "pixel", NULL };
static char *kwlist2[] = { "spec", NULL };
- int red = 0, green = 0, blue = 0;
- unsigned int pixel = 0;
- const char *spec;
+ PyObject *red = Py_None, *green = Py_None, *blue = Py_None;
+ const char *spec = NULL;
GdkColor colour;
- if (PyArg_ParseTupleAndKeywords(args, kwargs, "|iiik:gdk.Color", kwlist1,
- &red, &green, &blue, &pixel)) {
- colour.red = red;
- colour.green = green;
- colour.blue = blue;
- colour.pixel = pixel;
- goto success;
- }
+ /* Note: this constructor has become quite complicated, because it
+ * is heavily overloaded. Additionally, we try to optimize a
+ * little. */
- PyErr_Clear();
-
- if (PyArg_ParseTupleAndKeywords(args, kwargs, "s:color_parse|gdk.Color", kwlist2,
+ if (PyArg_ParseTupleAndKeywords(args, kwargs, "|s:gdk.Color", kwlist2,
&spec)) {
- if (!gdk_color_parse(spec, &colour)) {
+ if (!spec)
+ memset(&colour, 0, sizeof(colour));
+ else if (!gdk_color_parse(spec, &colour)) {
PyErr_SetString(PyExc_ValueError,
"unable to parse colour specification");
return -1;
@@ -60,6 +54,77 @@ _wrap_gdk_color_new(PyGBoxed *self,
}
PyErr_Clear();
+
+ if (PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOk:gdk.Color", kwlist1,
+ &red, &green, &blue, &colour.pixel)) {
+ /* We don't allow mixing floats and non-floats as that is too
+ * error-prone. All non-floats are deemed integers in case
+ * they have __int__() method. */
+ int have_floats = 0;
+ int have_nonfloats = 0;
+
+ if (red == Py_None)
+ colour.red = 0;
+ else {
+ if (PyFloat_Check(red)) {
+ have_floats = 1;
+ colour.red = MIN(MAX(0.0, PyFloat_AsDouble(red)), 1.0) * 65535.0;
+ }
+ else {
+ have_nonfloats = 1;
+ colour.red = PyInt_AsLong(red);
+ }
+ }
+
+ if (PyErr_Occurred())
+ return -1;
+
+ if (green == Py_None)
+ colour.green = 0;
+ else {
+ if (PyFloat_Check(green)) {
+ if (have_nonfloats)
+ goto mixed_types_error;
+ have_floats = 1;
+ colour.green = MIN(MAX(0.0, PyFloat_AsDouble(green)), 1.0) * 65535.0;
+ }
+ else {
+ if (have_floats)
+ goto mixed_types_error;
+ have_nonfloats = 1;
+ colour.green = PyInt_AsLong(green);
+ }
+ }
+
+ if (PyErr_Occurred())
+ return -1;
+
+ if (blue == Py_None)
+ colour.blue = 0;
+ else {
+ if (PyFloat_Check(blue)) {
+ if (have_nonfloats)
+ goto mixed_types_error;
+ colour.blue = MIN(MAX(0.0, PyFloat_AsDouble(blue)), 1.0) * 65535.0;
+ }
+ else {
+ if (have_floats)
+ goto mixed_types_error;
+ colour.blue = PyInt_AsLong(blue);
+ }
+ }
+
+ if (PyErr_Occurred())
+ return -1;
+
+ goto success;
+
+ mixed_types_error:
+ PyErr_SetString(PyExc_TypeError, "arguments must either be all integers or all floats");
+ return -1;
+ }
+
+ PyErr_Clear();
PyErr_SetString(PyExc_TypeError, "Usage:\n"
" gtk.gdk.Color(red, green, blue, pixel) [all are optional]\n"
" gtk.gdk.Color(spec) [see gtk.gdk.color_parse()]");
@@ -72,35 +137,118 @@ _wrap_gdk_color_new(PyGBoxed *self,
return 0;
}
+
%%
-override-slot GdkColor.tp_setattr
+override-attr GdkColor.red
static int
-_wrap_gdk_color_tp_setattr(PyGBoxed *self, char *attr, PyObject *value)
+_wrap_gdk_color__set_red(PyObject *self, PyObject *value, void *closure)
{
- if (value == NULL) {
- PyErr_SetString(PyExc_TypeError, "can't delete attributes");
+ long red = PyInt_AsLong(value);
+ if (red == -1 && PyErr_Occurred())
return -1;
+ else {
+ pyg_boxed_get(self, GdkColor)->red = red;
+ return 0;
}
+}
+%%
+override-attr GdkColor.blue
+static int
+_wrap_gdk_color__set_blue(PyObject *self, PyObject *value, void *closure)
+{
+ long blue = PyInt_AsLong(value);
+ if (blue == -1 && PyErr_Occurred())
+ return -1;
+ else {
+ pyg_boxed_get(self, GdkColor)->blue = blue;
+ return 0;
+ }
+}
+%%
+override-attr GdkColor.green
+static int
+_wrap_gdk_color__set_green(PyObject *self, PyObject *value, void *closure)
+{
+ long green = PyInt_AsLong(value);
+ if (green == -1 && PyErr_Occurred())
+ return -1;
+ else {
+ pyg_boxed_get(self, GdkColor)->green = green;
+ return 0;
+ }
+}
+%%
+override-attr GdkColor.pixel
+static int
+_wrap_gdk_color__set_pixel(PyObject *self, PyObject *value, void *closure)
+{
+ long pixel = PyInt_AsLong(value);
+ if (pixel == -1 && PyErr_Occurred())
+ return -1;
+ else {
+ pyg_boxed_get(self, GdkColor)->pixel = pixel;
+ return 0;
+ }
+}
+%%
+override-attr GdkColor.red_float
- if (PyInt_Check(value)) {
- int i = PyInt_AsLong(value);
- if (!strcmp(attr, "red")) {
- pyg_boxed_get(self, GdkColor)->red = i;
- return 0;
- } else if (!strcmp(attr, "green")) {
- pyg_boxed_get(self, GdkColor)->green = i;
- return 0;
- } else if (!strcmp(attr, "blue")) {
- pyg_boxed_get(self, GdkColor)->blue = i;
- return 0;
- } else if (!strcmp(attr, "pixel")) {
- pyg_boxed_get(self, GdkColor)->pixel = i;
- return 0;
- }
+static PyObject *
+_wrap_gdk_color__get_red_float(PyObject *self, void *closure)
+{
+ return PyFloat_FromDouble(pyg_boxed_get(self, GdkColor)->red / 65535.0);
+}
+
+static int
+_wrap_gdk_color__set_red_float(PyObject *self, PyObject *value, void *closure)
+{
+ double red = PyFloat_AsDouble(value);
+ if (red == -1 && PyErr_Occurred())
+ return -1;
+ else {
+ pyg_boxed_get(self, GdkColor)->red = MIN(MAX(0.0, red), 1.0) * 65535.0;
+ return 0;
}
+}
+%%
+override-attr GdkColor.green_float
- PyErr_SetString(PyExc_AttributeError, "could not write attribute");
- return -1;
+static PyObject *
+_wrap_gdk_color__get_green_float(PyObject *self, void *closure)
+{
+ return PyFloat_FromDouble(pyg_boxed_get(self, GdkColor)->green / 65535.0);
+}
+
+static int
+_wrap_gdk_color__set_green_float(PyObject *self, PyObject *value, void *closure)
+{
+ double green = PyFloat_AsDouble(value);
+ if (green == -1 && PyErr_Occurred())
+ return -1;
+ else {
+ pyg_boxed_get(self, GdkColor)->green = MIN(MAX(0.0, green), 1.0) * 65535.0;
+ return 0;
+ }
+}
+%%
+override-attr GdkColor.blue_float
+
+static PyObject *
+_wrap_gdk_color__get_blue_float(PyObject *self, void *closure)
+{
+ return PyFloat_FromDouble(pyg_boxed_get(self, GdkColor)->blue / 65535.0);
+}
+
+static int
+_wrap_gdk_color__set_blue_float(PyObject *self, PyObject *value, void *closure)
+{
+ double blue = PyFloat_AsDouble(value);
+ if (blue == -1 && PyErr_Occurred())
+ return -1;
+ else {
+ pyg_boxed_get(self, GdkColor)->blue = MIN(MAX(0.0, blue), 1.0) * 65535.0;
+ return 0;
+ }
}
%%
override gdk_color_parse kwargs
diff --git a/tests/test_color.py b/tests/test_color.py
index 72ad6693..d8484ae3 100644
--- a/tests/test_color.py
+++ b/tests/test_color.py
@@ -15,6 +15,14 @@ class Tests(unittest.TestCase):
self.assertEqual(c.green, 2)
self.assertEqual(c.blue, 3)
+ c = gtk.gdk.Color(1.0, 0.7, 0.2)
+ self.assertAlmostEqual(c.red_float, 1.0, 4)
+ self.assertAlmostEqual(c.green_float, 0.7, 4)
+ self.assertAlmostEqual(c.blue_float, 0.2, 4)
+
+ # Mixing integers and floats is not allowed.
+ self.assertRaises(TypeError, lambda: gtk.gdk.Color(0, 0.5))
+
c = gtk.gdk.Color(pixel=0xffff)
self.assertEqual(c.pixel, 0xffff)
@@ -36,6 +44,19 @@ class Tests(unittest.TestCase):
self.assertRaises(TypeError, lambda: gtk.gdk.Color([]))
+ def test_float_attribute(self):
+ c = gtk.gdk.Color(0, 10000, 65535)
+ self.assertAlmostEqual(c.red_float, 0.0)
+ self.assertAlmostEqual(c.green_float, 10000.0 / 65535.0)
+ self.assertAlmostEqual(c.blue_float, 1.0)
+
+ c.red_float = 0.57
+ self.assert_(c.red == int(0.57 * 65535) or c.red == int(0.57 * 65535) + 1)
+ self.assertAlmostEqual(c.red_float, 0.57, 4)
+
+ c.green = 12345
+ self.assertAlmostEqual(c.green_float, 12345.0 / 65535.0)
+
def test_equal(self):
self.assertEqual(gtk.gdk.Color(0, 0, 0), gtk.gdk.Color(0, 0, 0))
self.assertEqual(gtk.gdk.Color(100, 200, 300), gtk.gdk.Color(100, 200, 300))