Skip to content

bpo-33289: Return RGB triplet of ints instead of floats from tkinter.colorchooser #6578

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jan 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Lib/tkinter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1189,8 +1189,7 @@ def winfo_reqwidth(self):
self.tk.call('winfo', 'reqwidth', self._w))

def winfo_rgb(self, color):
"""Return tuple of decimal values for red, green, blue for
COLOR in this widget."""
"""Return a tuple of integer RGB values in range(65536) for color in this widget."""
return self._getints(
self.tk.call('winfo', 'rgb', self._w, color))

Expand Down
60 changes: 36 additions & 24 deletions Lib/tkinter/colorchooser.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,57 +8,69 @@
# fixed initialcolor handling in August 1998
#

#
# options (all have default values):
#
# - initialcolor: color to mark as selected when dialog is displayed
# (given as an RGB triplet or a Tk color string)
#
# - parent: which window to place the dialog on top of
#
# - title: dialog title
#

from tkinter.commondialog import Dialog

__all__ = ["Chooser", "askcolor"]


#
# color chooser class

class Chooser(Dialog):
"Ask for a color"
"""Create a dialog for the tk_chooseColor command.

Args:
master: The master widget for this dialog. If not provided,
defaults to options['parent'] (if defined).
options: Dictionary of options for the tk_chooseColor call.
initialcolor: Specifies the selected color when the
dialog is first displayed. This can be a tk color
string or a 3-tuple of ints in the range (0, 255)
for an RGB triplet.
parent: The parent window of the color dialog. The
color dialog is displayed on top of this.
title: A string for the title of the dialog box.
"""

command = "tk_chooseColor"

def _fixoptions(self):
"""Ensure initialcolor is a tk color string.

Convert initialcolor from a RGB triplet to a color string.
"""
try:
# make sure initialcolor is a tk color string
color = self.options["initialcolor"]
if isinstance(color, tuple):
# assume an RGB triplet
# Assume an RGB triplet.
self.options["initialcolor"] = "#%02x%02x%02x" % color
except KeyError:
pass

def _fixresult(self, widget, result):
# result can be somethings: an empty tuple, an empty string or
# a Tcl_Obj, so this somewhat weird check handles that
"""Adjust result returned from call to tk_chooseColor.

Return both an RGB tuple of ints in the range (0, 255) and the
tk color string in the form #rrggbb.
"""
# Result can be many things: an empty tuple, an empty string, or
# a _tkinter.Tcl_Obj, so this somewhat weird check handles that.
if not result or not str(result):
return None, None # canceled
return None, None # canceled

# to simplify application code, the color chooser returns
# an RGB tuple together with the Tk color string
# To simplify application code, the color chooser returns
# an RGB tuple together with the Tk color string.
r, g, b = widget.winfo_rgb(result)
return (r/256, g/256, b/256), str(result)
return (r//256, g//256, b//256), str(result)


#
# convenience stuff

def askcolor(color = None, **options):
"Ask for a color"
def askcolor(color=None, **options):
"""Display dialog window for selection of a color.

Convenience wrapper for the Chooser class. Displays the color
chooser dialog with color as the initial value.
"""

if color:
options = options.copy()
Expand Down
37 changes: 34 additions & 3 deletions Lib/tkinter/test/test_tkinter/test_colorchooser.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,44 @@
import unittest
import tkinter
from test.support import requires, run_unittest, swap_attr
from tkinter.test.support import AbstractDefaultRootTest
from tkinter.commondialog import Dialog
from tkinter.test.support import AbstractDefaultRootTest, AbstractTkTest
from tkinter import colorchooser
from tkinter.colorchooser import askcolor
from tkinter.commondialog import Dialog

requires('gui')


class ChooserTest(AbstractTkTest, unittest.TestCase):

@classmethod
def setUpClass(cls):
AbstractTkTest.setUpClass.__func__(cls)
cls.cc = colorchooser.Chooser(initialcolor='dark blue slate')

def test_fixoptions(self):
cc = self.cc
cc._fixoptions()
self.assertEqual(cc.options['initialcolor'], 'dark blue slate')

cc.options['initialcolor'] = '#D2D269691E1E'
cc._fixoptions()
self.assertEqual(cc.options['initialcolor'], '#D2D269691E1E')

cc.options['initialcolor'] = (210, 105, 30)
cc._fixoptions()
self.assertEqual(cc.options['initialcolor'], '#d2691e')

def test_fixresult(self):
cc = self.cc
self.assertEqual(cc._fixresult(self.root, ()), (None, None))
self.assertEqual(cc._fixresult(self.root, ''), (None, None))
self.assertEqual(cc._fixresult(self.root, 'chocolate'),
((210, 105, 30), 'chocolate'))
self.assertEqual(cc._fixresult(self.root, '#4a3c8c'),
((74, 60, 140), '#4a3c8c'))


class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):

def test_askcolor(self):
Expand All @@ -33,7 +64,7 @@ def test_callback(dialog, master):
self.assertRaises(RuntimeError, askcolor)


tests_gui = (DefaultRootTest,)
tests_gui = (ChooserTest, DefaultRootTest,)

if __name__ == "__main__":
run_unittest(*tests_gui)
20 changes: 20 additions & 0 deletions Lib/tkinter/test/test_tkinter/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,26 @@ def test_clipboard_astral(self):
with self.assertRaises(tkinter.TclError):
root.clipboard_get()

def test_winfo_rgb(self):
root = self.root
rgb = root.winfo_rgb

# Color name.
self.assertEqual(rgb('red'), (65535, 0, 0))
self.assertEqual(rgb('dark slate blue'), (18504, 15677, 35723))
# #RGB - extends each 4-bit hex value to be 16-bit.
self.assertEqual(rgb('#F0F'), (0xFFFF, 0x0000, 0xFFFF))
# #RRGGBB - extends each 8-bit hex value to be 16-bit.
self.assertEqual(rgb('#4a3c8c'), (0x4a4a, 0x3c3c, 0x8c8c))
# #RRRRGGGGBBBB
self.assertEqual(rgb('#dede14143939'), (0xdede, 0x1414, 0x3939))
# Invalid string.
with self.assertRaises(tkinter.TclError):
rgb('#123456789a')
# RGB triplet is invalid input.
with self.assertRaises(tkinter.TclError):
rgb((111, 78, 55))

def test_event_repr_defaults(self):
e = tkinter.Event()
e.serial = 12345
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Correct call to :mod:`tkinter.colorchooser` to return RGB triplet of ints
instead of floats. Patch by Cheryl Sabella.