diff options
author | Paul Pogonyshev <pogonyshev@gmx.net> | 2009-05-12 00:47:51 +0300 |
---|---|---|
committer | Paul Pogonyshev <pogonyshev@gmx.net> | 2009-05-15 22:49:56 +0300 |
commit | 15b7265a00d405d6115f45ba0b1f26019ef1a52b (patch) | |
tree | 647ba8393bd2279b63099171398a5d45a2e9bc51 | |
parent | 029b2905baec78b370e76e1b2340dc2ee2ddaea1 (diff) | |
download | pygtk-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.xml | 50 | ||||
-rw-r--r-- | gtk/gdk-base-types.defs | 5 | ||||
-rw-r--r-- | gtk/gdkcolor.override | 220 | ||||
-rw-r--r-- | tests/test_color.py | 21 |
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)) |