Skip to content

Commit 6713e86

Browse files
authored
bpo-33289: Return RGB triplet of ints instead of floats from tkinter.colorchooser (GH-6578)
1 parent 805ede8 commit 6713e86

File tree

5 files changed

+93
-29
lines changed

5 files changed

+93
-29
lines changed

Lib/tkinter/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,8 +1185,7 @@ def winfo_reqwidth(self):
11851185
self.tk.call('winfo', 'reqwidth', self._w))
11861186

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

Lib/tkinter/colorchooser.py

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,57 +8,69 @@
88
# fixed initialcolor handling in August 1998
99
#
1010

11-
#
12-
# options (all have default values):
13-
#
14-
# - initialcolor: color to mark as selected when dialog is displayed
15-
# (given as an RGB triplet or a Tk color string)
16-
#
17-
# - parent: which window to place the dialog on top of
18-
#
19-
# - title: dialog title
20-
#
2111

2212
from tkinter.commondialog import Dialog
2313

2414
__all__ = ["Chooser", "askcolor"]
2515

2616

27-
#
28-
# color chooser class
29-
3017
class Chooser(Dialog):
31-
"Ask for a color"
18+
"""Create a dialog for the tk_chooseColor command.
19+
20+
Args:
21+
master: The master widget for this dialog. If not provided,
22+
defaults to options['parent'] (if defined).
23+
options: Dictionary of options for the tk_chooseColor call.
24+
initialcolor: Specifies the selected color when the
25+
dialog is first displayed. This can be a tk color
26+
string or a 3-tuple of ints in the range (0, 255)
27+
for an RGB triplet.
28+
parent: The parent window of the color dialog. The
29+
color dialog is displayed on top of this.
30+
title: A string for the title of the dialog box.
31+
"""
3232

3333
command = "tk_chooseColor"
3434

3535
def _fixoptions(self):
36+
"""Ensure initialcolor is a tk color string.
37+
38+
Convert initialcolor from a RGB triplet to a color string.
39+
"""
3640
try:
37-
# make sure initialcolor is a tk color string
3841
color = self.options["initialcolor"]
3942
if isinstance(color, tuple):
40-
# assume an RGB triplet
43+
# Assume an RGB triplet.
4144
self.options["initialcolor"] = "#%02x%02x%02x" % color
4245
except KeyError:
4346
pass
4447

4548
def _fixresult(self, widget, result):
46-
# result can be somethings: an empty tuple, an empty string or
47-
# a Tcl_Obj, so this somewhat weird check handles that
49+
"""Adjust result returned from call to tk_chooseColor.
50+
51+
Return both an RGB tuple of ints in the range (0, 255) and the
52+
tk color string in the form #rrggbb.
53+
"""
54+
# Result can be many things: an empty tuple, an empty string, or
55+
# a _tkinter.Tcl_Obj, so this somewhat weird check handles that.
4856
if not result or not str(result):
49-
return None, None # canceled
57+
return None, None # canceled
5058

51-
# to simplify application code, the color chooser returns
52-
# an RGB tuple together with the Tk color string
59+
# To simplify application code, the color chooser returns
60+
# an RGB tuple together with the Tk color string.
5361
r, g, b = widget.winfo_rgb(result)
54-
return (r/256, g/256, b/256), str(result)
62+
return (r//256, g//256, b//256), str(result)
5563

5664

5765
#
5866
# convenience stuff
5967

60-
def askcolor(color = None, **options):
61-
"Ask for a color"
68+
def askcolor(color=None, **options):
69+
"""Display dialog window for selection of a color.
70+
71+
Convenience wrapper for the Chooser class. Displays the color
72+
chooser dialog with color as the initial value.
73+
"""
6274

6375
if color:
6476
options = options.copy()

Lib/tkinter/test/test_tkinter/test_colorchooser.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,44 @@
11
import unittest
22
import tkinter
33
from test.support import requires, run_unittest, swap_attr
4-
from tkinter.test.support import AbstractDefaultRootTest
5-
from tkinter.commondialog import Dialog
4+
from tkinter.test.support import AbstractDefaultRootTest, AbstractTkTest
5+
from tkinter import colorchooser
66
from tkinter.colorchooser import askcolor
7+
from tkinter.commondialog import Dialog
78

89
requires('gui')
910

1011

12+
class ChooserTest(AbstractTkTest, unittest.TestCase):
13+
14+
@classmethod
15+
def setUpClass(cls):
16+
AbstractTkTest.setUpClass.__func__(cls)
17+
cls.cc = colorchooser.Chooser(initialcolor='dark blue slate')
18+
19+
def test_fixoptions(self):
20+
cc = self.cc
21+
cc._fixoptions()
22+
self.assertEqual(cc.options['initialcolor'], 'dark blue slate')
23+
24+
cc.options['initialcolor'] = '#D2D269691E1E'
25+
cc._fixoptions()
26+
self.assertEqual(cc.options['initialcolor'], '#D2D269691E1E')
27+
28+
cc.options['initialcolor'] = (210, 105, 30)
29+
cc._fixoptions()
30+
self.assertEqual(cc.options['initialcolor'], '#d2691e')
31+
32+
def test_fixresult(self):
33+
cc = self.cc
34+
self.assertEqual(cc._fixresult(self.root, ()), (None, None))
35+
self.assertEqual(cc._fixresult(self.root, ''), (None, None))
36+
self.assertEqual(cc._fixresult(self.root, 'chocolate'),
37+
((210, 105, 30), 'chocolate'))
38+
self.assertEqual(cc._fixresult(self.root, '#4a3c8c'),
39+
((74, 60, 140), '#4a3c8c'))
40+
41+
1142
class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
1243

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

3566

36-
tests_gui = (DefaultRootTest,)
67+
tests_gui = (ChooserTest, DefaultRootTest,)
3768

3869
if __name__ == "__main__":
3970
run_unittest(*tests_gui)

Lib/tkinter/test/test_tkinter/test_misc.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,26 @@ def test_clipboard_astral(self):
192192
with self.assertRaises(tkinter.TclError):
193193
root.clipboard_get()
194194

195+
def test_winfo_rgb(self):
196+
root = self.root
197+
rgb = root.winfo_rgb
198+
199+
# Color name.
200+
self.assertEqual(rgb('red'), (65535, 0, 0))
201+
self.assertEqual(rgb('dark slate blue'), (18504, 15677, 35723))
202+
# #RGB - extends each 4-bit hex value to be 16-bit.
203+
self.assertEqual(rgb('#F0F'), (0xFFFF, 0x0000, 0xFFFF))
204+
# #RRGGBB - extends each 8-bit hex value to be 16-bit.
205+
self.assertEqual(rgb('#4a3c8c'), (0x4a4a, 0x3c3c, 0x8c8c))
206+
# #RRRRGGGGBBBB
207+
self.assertEqual(rgb('#dede14143939'), (0xdede, 0x1414, 0x3939))
208+
# Invalid string.
209+
with self.assertRaises(tkinter.TclError):
210+
rgb('#123456789a')
211+
# RGB triplet is invalid input.
212+
with self.assertRaises(tkinter.TclError):
213+
rgb((111, 78, 55))
214+
195215
def test_event_repr_defaults(self):
196216
e = tkinter.Event()
197217
e.serial = 12345
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Correct call to :mod:`tkinter.colorchooser` to return RGB triplet of ints
2+
instead of floats. Patch by Cheryl Sabella.

0 commit comments

Comments
 (0)