diff options
-rw-r--r-- | docs/reference/pygtk-gdkcolor.xml | 100 | ||||
-rw-r--r-- | gtk/gdk-base-types.defs | 3 | ||||
-rw-r--r-- | gtk/gdkcolor.override | 83 | ||||
-rw-r--r-- | tests/test_color.py | 21 |
4 files changed, 204 insertions, 3 deletions
diff --git a/docs/reference/pygtk-gdkcolor.xml b/docs/reference/pygtk-gdkcolor.xml index 82bd82b0..7a54716c 100644 --- a/docs/reference/pygtk-gdkcolor.xml +++ b/docs/reference/pygtk-gdkcolor.xml @@ -44,7 +44,12 @@ linkend="constructor-gdkcolor">gtk.gdk.Color</link></methodname> <methodsynopsis language="python"> <methodname><link linkend="function-gdk--color-parse">gtk.gdk.color_parse</link></methodname> <methodparam><parameter role="keyword">spec</parameter></methodparam> - </methodsynopsis></programlisting> + </methodsynopsis><methodsynopsis language="python"> + <methodname><link linkend="function-gdk--color-from-hsv">gtk.gdk.color_from_hsv</link></methodname> + <methodparam><parameter role="keyword">hue</parameter></methodparam> + <methodparam><parameter role="keyword">saturation</parameter></methodparam> + <methodparam><parameter role="keyword">value</parameter></methodparam> + </methodsynopsis></programlisting> </refsect1> @@ -52,7 +57,7 @@ linkend="constructor-gdkcolor">gtk.gdk.Color</link></methodname> <title>Attributes</title> <note> - <para>Floating-point attributes are available in PyGTK 2.16 and above.</para> + <para>Floating-point and HSV attributes are available in PyGTK 2.16 and above.</para> </note> <blockquote role="properties"> @@ -106,6 +111,24 @@ linkend="constructor-gdkcolor">gtk.gdk.Color</link></methodname> <entry>The value of the blue component of the color as a float in the range 0.0--1.0</entry> </row> + <row valign="top"> + <entry>"hue"</entry> + <entry>Read</entry> + <entry>The hue (in HSV colorspace) of the color as a float in the range 0.0--1.0</entry> + </row> + + <row valign="top"> + <entry>"saturation"</entry> + <entry>Read</entry> + <entry>The saturation (in HSV colorspace) of the color as a float in the range 0.0--1.0</entry> + </row> + + <row valign="top"> + <entry>"value"</entry> + <entry>Read</entry> + <entry>The value (in HSV colorspace) of the color as a float in the range 0.0--1.0</entry> + </row> + </tbody> </tgroup> </informaltable> @@ -151,6 +174,17 @@ object.</para> color == eval(repr(color)) </programlisting> + <para> + PyGTK 2.16 introduces several ways of using floating-point numbers for + creating <link linkend="class-gdkcolor"><classname>gtk.gdk.Color</classname></link> + objects or setting their individual components. In all cases it was decided to + silently clamp input values to valid range, rather than being strict and e.g. raise + an exception. The rationale is that floating-point arithmetics are imprecise, so + you could end with a computed value slightly larger than maximum or a little smaller + than minimum valid value. To simplify using new features, such "slightly off" + values are just clamped without any warning. + </para> + </refsect1> <refsect1 id="constructor-gdkcolor"> @@ -334,6 +368,68 @@ linkend="class-gdkcolor"><classname>gtk.gdk.Color</classname></link> is </refsect2> + <refsect2 id="function-gdk--color-from-hsv"> + <title>gtk.gdk.color_from_hsv</title> + + <programlisting><methodsynopsis language="python"> + <methodname>gtk.gdk.color_from_hsv</methodname> + <methodparam><parameter role="keyword">hue</parameter></methodparam> + <methodparam><parameter role="keyword">saturation</parameter></methodparam> + <methodparam><parameter role="keyword">value</parameter></methodparam> + </methodsynopsis></programlisting> + + <variablelist> + <varlistentry> + <term><parameter role="keyword">hue</parameter> :</term> + <listitem><simpara>Hue of the desired color as a float in range + 0.0--1.0</simpara></listitem> + </varlistentry> + <varlistentry> + <term><parameter role="keyword">saturation</parameter> :</term> + <listitem><simpara>Saturation of the desired color as a float in range + 0.0--1.0</simpara></listitem> + </varlistentry> + <varlistentry> + <term><parameter role="keyword">value</parameter> :</term> + <listitem><simpara>Value of the desired color as a float in range + 0.0--1.0</simpara></listitem> + </varlistentry> + <varlistentry> + <term><emphasis>Returns</emphasis> :</term> + <listitem><simpara>a <link linkend="class-gdkcolor"><classname>gtk.gdk.Color</classname></link> + object</simpara></listitem> + </varlistentry> + </variablelist> + + <note> + <para>This function is available in PyGTK 2.16 and above.</para> + </note> + + <para>The <function>gtk.gdk.color_from_hsv</function>() method returns + the <link linkend="class-gdkcolor"><classname>gtk.gdk.Color</classname></link> + specified by the HSV parameters. All three parameters are mandatory and should be + floats from 0.0 to 1.0. The range requirement, however, is not strict, see + below.</para> + + <para>As hue goes from 0 to 1 color goes roughly as red → yellow → green → cyan → + blue → magenta → red. Because of the "circular" nature, this parameter wraps + around, so only fractional part matters. E.g. -4.2 or 1.8 are the same as 0.8. + Saturation determines how intense a color is, with 0.0 meaning pure gray and 1.0 + -- fully intense color. Value determines how light a color is, with 0.0 standing + for fully black and 1.0 for completely white color. Both saturation and value are + clamped to valid range (see rationale at the top of the page).</para> + + <para>Note that internal storage is still integers, so values of + corresponding <literal>hue</literal>, <literal>saturation</literal> + and <literal>value</literal> attributes of the returned object will not + necessarily be equal to the value used as argument for this functions. They will + be as close as permitted by 16-bit color component storage used + by <classname>GdkColor</classname> though.</para> + + <para>For more details read about <ulink url="http://en.wikipedia.org/wiki/HSL_and_HSV">HSV + colorspace</ulink>.</para> + </refsect2> + </refsect1> </refentry> diff --git a/gtk/gdk-base-types.defs b/gtk/gdk-base-types.defs index f6799bf2..5f395344 100644 --- a/gtk/gdk-base-types.defs +++ b/gtk/gdk-base-types.defs @@ -233,6 +233,9 @@ '("gfloat" "red_float") '("gfloat" "green_float") '("gfloat" "blue_float") + '("gfloat" "hue") + '("gfloat" "saturation") + '("gfloat" "value") ) ) diff --git a/gtk/gdkcolor.override b/gtk/gdkcolor.override index f46b8783..6ac88814 100644 --- a/gtk/gdkcolor.override +++ b/gtk/gdkcolor.override @@ -139,6 +139,44 @@ _wrap_gdk_color_new(PyGBoxed *self, } %% +define color_from_hsv +static PyObject * +_wrap_color_from_hsv (PyObject *ignored, PyObject *args, PyObject*kwargs) +{ + static char *kwlist[] = { "hue", "saturation", "value", NULL }; + gdouble hue, saturation, value; + gdouble red, green, blue; + GdkColor color; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ddd:gtk.gdk.color_from_hsv", kwlist, + &hue, &saturation, &value)) + return NULL; + + /* See documentation of the class for rationale. */ + + hue -= floor(hue); + + if (saturation > 1.0) + saturation = 1.0; + else if (saturation < 0.0) + saturation = 0.0; + + if (value > 1.0) + value = 1.0; + else if (value < 0.0) + value = 0.0; + + gtk_hsv_to_rgb(hue, saturation, value, + &red, &green, &blue); + + color.red = red * 65535.0; + color.green = green * 65535.0; + color.blue = blue * 65535.0; + + return pyg_boxed_new(GDK_TYPE_COLOR, &color, TRUE, TRUE); +} + +%% override-attr GdkColor.red static int _wrap_gdk_color__set_red(PyObject *self, PyObject *value, void *closure) @@ -251,6 +289,51 @@ _wrap_gdk_color__set_blue_float(PyObject *self, PyObject *value, void *closure) } } %% +override-attr GdkColor.hue + +static PyObject * +_wrap_gdk_color__get_hue(PyObject *self, void *closure) +{ + GdkColor *color = pyg_boxed_get(self, GdkColor); + gdouble red = color->red / 65535.0; + gdouble green = color->green / 65535.0; + gdouble blue = color->blue / 65535.0; + gdouble hue; + + gtk_rgb_to_hsv(red, green, blue, &hue, NULL, NULL); + return PyFloat_FromDouble(hue); +} +%% +override-attr GdkColor.saturation + +static PyObject * +_wrap_gdk_color__get_saturation(PyObject *self, void *closure) +{ + GdkColor *color = pyg_boxed_get(self, GdkColor); + gdouble red = color->red / 65535.0; + gdouble green = color->green / 65535.0; + gdouble blue = color->blue / 65535.0; + gdouble saturation; + + gtk_rgb_to_hsv(red, green, blue, NULL, &saturation, NULL); + return PyFloat_FromDouble(saturation); +} +%% +override-attr GdkColor.value + +static PyObject * +_wrap_gdk_color__get_value(PyObject *self, void *closure) +{ + GdkColor *color = pyg_boxed_get(self, GdkColor); + gdouble red = color->red / 65535.0; + gdouble green = color->green / 65535.0; + gdouble blue = color->blue / 65535.0; + gdouble value; + + gtk_rgb_to_hsv(red, green, blue, NULL, NULL, &value); + return PyFloat_FromDouble(value); +} +%% override gdk_color_parse kwargs static PyObject * _wrap_gdk_color_parse(PyObject *self, PyObject *args, PyObject *kwargs) diff --git a/tests/test_color.py b/tests/test_color.py index d8484ae3..82f35c79 100644 --- a/tests/test_color.py +++ b/tests/test_color.py @@ -44,7 +44,10 @@ class Tests(unittest.TestCase): self.assertRaises(TypeError, lambda: gtk.gdk.Color([])) - def test_float_attribute(self): + def test_color_from_hsv(self): + self.assertEqual(gtk.gdk.Color('red'), gtk.gdk.color_from_hsv(0.0, 1.0, 1.0)) + + def test_float_attributes(self): c = gtk.gdk.Color(0, 10000, 65535) self.assertAlmostEqual(c.red_float, 0.0) self.assertAlmostEqual(c.green_float, 10000.0 / 65535.0) @@ -57,6 +60,22 @@ class Tests(unittest.TestCase): c.green = 12345 self.assertAlmostEqual(c.green_float, 12345.0 / 65535.0) + def test_hue(self): + self.assertAlmostEqual(gtk.gdk.Color('red').hue, 0 * 1.0 / 6) + self.assertAlmostEqual(gtk.gdk.Color('yellow').hue, 1 * 1.0 / 6) + self.assertAlmostEqual(gtk.gdk.Color('green').hue, 2 * 1.0 / 6) + self.assertAlmostEqual(gtk.gdk.Color('cyan').hue, 3 * 1.0 / 6) + self.assertAlmostEqual(gtk.gdk.Color('blue').hue, 4 * 1.0 / 6) + self.assertAlmostEqual(gtk.gdk.Color('magenta').hue, 5 * 1.0 / 6) + + def test_saturation(self): + self.assertAlmostEqual(gtk.gdk.Color('red').saturation, 1.0) + self.assertAlmostEqual(gtk.gdk.Color('gray').saturation, 0.0) + + def test_value(self): + self.assertAlmostEqual(gtk.gdk.Color('black').value, 0.0) + self.assertAlmostEqual(gtk.gdk.Color('white').value, 1.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)) |