classification
Title: tkinter askcolor returning floats for r, g, b values instead of ints
Type: behavior Stage: patch review
Components: Tkinter Versions: Python 3.10
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Bryan.Oakley, cheryl.sabella, serhiy.storchaka, terry.reedy, vstinner
Priority: normal Keywords: patch

Created on 2018-04-16 21:49 by Bryan.Oakley, last changed 2021-01-21 19:14 by serhiy.storchaka.

Files
File name Uploaded Description Edit
tk_rgb.py terry.reedy, 2021-01-21 17:31
Pull Requests
URL Status Linked Edit
PR 6578 merged cheryl.sabella, 2018-04-23 17:46
Messages (12)
msg315367 - (view) Author: Bryan Oakley (Bryan.Oakley) Date: 2018-04-16 21:49
Even though the underlying tcl/tk interpreter is returning ints, askcolor is converting the values to floats. My guess is this is an oversight related to the change in functionality of the / operator in python3.

this:

    return (r/256, g/256, b/256), str(result)

should probably be this:

    return (r//256, g//256, b//256), str(result)
msg315548 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2018-04-21 03:24
For future reference, 3.4 and 3.5 only get security fixes.

The quoted line of code is from the main part of tkinter.colorchooser,Chooser._fixresult:
        r, g, b = widget.winfo_rgb(result)
        return (r/256, g/256, b/256), str(result)

where tkinter.Misc defines
    def winfo_rgb(self, color):
        """Return tuple of decimal values for red, green, blue for
        COLOR in this widget."""
        return self._getints(
            self.tk.call('winfo', 'rgb', self._w, color))

The code in tkColorChooser and Tkinter in 2.x is the same.

The docstring for winfo_rgb is wrong as it returns a tuple of ints in range(2**16256).  For red, the results are
>>> Tk().winfo_rgb('red')
(65535, 0, 0)  # 2.x and 3.x
>>> cc.askcolor('red')
((255, 0, 0), '#ff0000')  # 2.7
>>> cc.askcolor('red')
((255.99609375, 0.0, 0.0), '#ff0000')  # 3.8

In addition to fixing the winfo_rgb docstring (in all versions), and adding doc strings to colorchooser, it seems that '/' should be '//' in 3.x.  I don't know if I would fix this in 3.6.
msg315555 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2018-04-21 07:35
Good catch! A color tuple will likely be used in '#%02x%02x%02x' % color, and this will fail because %x works only with integers (in general sense). Therefore returning a tuple of floats is a bug.

The downside is that we loss some information. Tk supports up to 16 bit per color component, and askcolor() keeps only higher 8 of them. This can't be changed for backward compatibility, but it may be worth to add an option for control the representation of the result. winfo_rgb() returns 16-bit color components. This is a separate issue.
msg315619 - (view) Author: Cheryl Sabella (cheryl.sabella) * (Python committer) Date: 2018-04-22 15:43
Adding to what Serhiy said about the askcolor() return value is an issue that also affects input:


>>> cc.askcolor('red')
((255, 0, 0), '#ff0000')
>>> cc.askcolor((255, 0, 0))
((255, 0, 0), '#ff0000')
>>> cc.askcolor((65535, 0, 0))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/cheryl/cpython/Lib/tkinter/colorchooser.py", line 75, in askcolor
    return Chooser(**options).show()
  File "/home/cheryl/cpython/Lib/tkinter/commondialog.py", line 43, in show
    s = w.tk.call(self.command, *w._options(self.options))
_tkinter.TclError: invalid color name "#ffff0000"


Changing _fixoptions to format initialcolor using "#%04x%04x%04x" % color.

allows for 16-bit hex values, but it's not the same as 8-bit.

>>> cc.askcolor('red')
((255, 0, 0), '#ff0000')
>>> cc.askcolor((255, 0, 0))
((0, 0, 0), '#000000')
>>> cc.askcolor((65535, 0, 0))
((255, 0, 0), '#ff0000')

(255, 0, 0) in the second call is black and not red.
msg321551 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2018-07-12 13:48
Is this issue a regression of Python 3? red/256 gave an integer on Python 2?
msg321559 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2018-07-12 14:53
It appears so.
msg321564 - (view) Author: Bryan Oakley (Bryan.Oakley) Date: 2018-07-12 15:22
yes, this is a well known backwards incompatibility. In python 2, the
division operator returns an integer if both operands are integers. In
python 3 it returns a float.

https://www.python.org/dev/peps/pep-0238/

On Thu, Jul 12, 2018 at 8:48 AM STINNER Victor <report@bugs.python.org>
wrote:

>
> STINNER Victor <vstinner@redhat.com> added the comment:
>
> Is this issue a regression of Python 3? red/256 gave an integer on Python
> 2?
>
> ----------
> nosy: +vstinner
>
> _______________________________________
> Python tracker <report@bugs.python.org>
> <https://bugs.python.org/issue33289>
> _______________________________________
>
msg385400 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2021-01-21 09:19
https://www.tcl.tk/man/tcl8.6/TkCmd/winfo.htm says
"winfo rgb window color
    Returns a list containing three decimal values in the range 0 to 65535, which are the red, green, and blue intensities that correspond to color in the window given by window. Color may be specified in any of the forms acceptable for a color option."

Since tk represents data as strings, 'decimal value in [0, 65535)' means 'integer value represented by decimal digits'.  'correspond to color in the window' implies that the mapping from color to decimal value could depend on the window.  The mapping *does* differ between systems (see below).

https://www.tcl.tk/man/tcl8.6/TkLib/GetColor.htm
Lists possible return values, hence acceptible forms, as name or '#' followed by 1 to 4 hex digits.  It continues with "Each R, G, or B represents a single hexadecimal digit. The four forms permit colors to be specified with 4-bit, 8-bit, 12-bit or 16-bit values. When fewer than 16 bits are provided for each color, they represent the most significant bits of the color, while the lower unfilled bits will be repeatedly replicated from the available higher bits. For example, #3a7 is the same as #3333aaaa7777."

This omits to say what happens when there are 2 or 3 hex digits.  Windows ignores 3rd and 4th hex digits.  On Windows, dividing by 257 instead of 256 would be correct.  However, 256 works because the 'error', represented by the remainder, is always in [0, 256).  MacOS does not ignore digits.

https://www.tcl.tk/man/tcl8.6/TkCmd/colors.htm
Lists current color names and their values as 16 bit decimals.

The PR is blocked because the new colorchooser tests pass on Windows and macOS but fail on Ubuntu.  Cheryl speculated that the color names values vary across systems.  I think that it might instead be a coding issue, but I want to understand coding on Windows and Mac better before looking into Linux.
msg385434 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2021-01-21 17:31
65535 = 35536 - 1 = 256 * 256 - 1 == 255 * 257

On Windows, each r, g, b value is n * 257 for n in range(256) (see attached file).  The precision loss happens when colors are stored, before the division in winfo_rgb.  Perhaps 8 bits/channel (including alpha) is baked in.

Since divmod(n * 257, 257) = (n, 0) and divmod(n * 257, 256) = (n, n),
(n*257) // m = divmod(n*257, m)[0] = n whether m is 256 or 257.
---

macOS appears (from limited experiments) to handle 1 and 2 digits the same as Windows (repeat 4 or 2 times): val('#a00') = val('#aaaa00000000') = 0xaaaa, val('#ab0000') = val('#abab00000000') = 0xabab.  4 digits are left alone while 3 digits use the 1st as the 4th val('#abc000000') = val('#abca00000000') = 0xabca.
msg385436 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2021-01-21 17:46
I do not understand why #abc000000 and #abcd00000000 give 0xabab on my computer (Linux) and even weirder result on Ubuntu on CI. Reading the code I expected the same behavior as on macOS.
msg385438 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2021-01-21 18:09
Your Linux result is the same as on Windows. Given strings 'abc' or 'abcd', ignore 'c' or 'cd' and expand 'ab' to 'abab', making value 0xabab.  Is your computer Ubuntu (implying that personal Ubuntu != CI Ubuntu) or a different Linux?  Are there tk/tcl compilation flags or X window options that could affect stored color values?
msg385441 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2021-01-21 19:14
New changeset 6713e869c4989c04318158b406c30a147ea52904 by Cheryl Sabella in branch 'master':
bpo-33289: Return RGB triplet of ints instead of floats from tkinter.colorchooser (GH-6578)
https://github.com/python/cpython/commit/6713e869c4989c04318158b406c30a147ea52904
History
Date User Action Args
2021-01-21 19:14:12serhiy.storchakasetmessages: + msg385441
2021-01-21 18:09:26terry.reedysetmessages: + msg385438
2021-01-21 17:46:33serhiy.storchakasetmessages: + msg385436
2021-01-21 17:31:51terry.reedysetfiles: + tk_rgb.py

messages: + msg385434
2021-01-21 09:19:16terry.reedysetmessages: + msg385400
versions: + Python 3.10, - Python 3.6, Python 3.7, Python 3.8
2018-07-28 12:47:32steve.dowersetkeywords: - easy
2018-07-12 15:22:48Bryan.Oakleysetmessages: + msg321564
title: tkinter askcolor returning floats for r,g,b values instead of ints -> tkinter askcolor returning floats for r, g, b values instead of ints
2018-07-12 14:53:11terry.reedysetmessages: + msg321559
2018-07-12 13:48:53vstinnersetnosy: + vstinner
messages: + msg321551
2018-04-23 17:46:51cheryl.sabellasetkeywords: + patch
stage: needs patch -> patch review
pull_requests: + pull_request6278
2018-04-22 15:43:03cheryl.sabellasetnosy: + cheryl.sabella
messages: + msg315619
2018-04-21 07:36:05serhiy.storchakasetstage: needs patch
2018-04-21 07:35:56serhiy.storchakasetkeywords: + easy

messages: + msg315555
2018-04-21 03:24:52terry.reedysetnosy: + serhiy.storchaka, terry.reedy
title: askcolor is returning floats for r,g,b values instead of ints -> tkinter askcolor returning floats for r,g,b values instead of ints
messages: + msg315548

versions: - Python 3.4, Python 3.5
type: behavior
2018-04-16 21:49:41Bryan.Oakleycreate