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

This commit is contained in:
Cheryl Sabella 2021-01-21 14:14:04 -05:00 committed by GitHub
parent 805ede8ae8
commit 6713e869c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 94 additions and 30 deletions

View file

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

View file

@ -8,57 +8,69 @@
# fixed initialcolor handling in August 1998 # 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 from tkinter.commondialog import Dialog
__all__ = ["Chooser", "askcolor"] __all__ = ["Chooser", "askcolor"]
#
# color chooser class
class Chooser(Dialog): 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" command = "tk_chooseColor"
def _fixoptions(self): def _fixoptions(self):
"""Ensure initialcolor is a tk color string.
Convert initialcolor from a RGB triplet to a color string.
"""
try: try:
# make sure initialcolor is a tk color string
color = self.options["initialcolor"] color = self.options["initialcolor"]
if isinstance(color, tuple): if isinstance(color, tuple):
# assume an RGB triplet # Assume an RGB triplet.
self.options["initialcolor"] = "#%02x%02x%02x" % color self.options["initialcolor"] = "#%02x%02x%02x" % color
except KeyError: except KeyError:
pass pass
def _fixresult(self, widget, result): def _fixresult(self, widget, result):
# result can be somethings: an empty tuple, an empty string or """Adjust result returned from call to tk_chooseColor.
# a Tcl_Obj, so this somewhat weird check handles that
if not result or not str(result):
return None, None # canceled
# to simplify application code, the color chooser returns Return both an RGB tuple of ints in the range (0, 255) and the
# an RGB tuple together with the Tk color string 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
# To simplify application code, the color chooser returns
# an RGB tuple together with the Tk color string.
r, g, b = widget.winfo_rgb(result) 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 # convenience stuff
def askcolor(color = None, **options): def askcolor(color=None, **options):
"Ask for a color" """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: if color:
options = options.copy() options = options.copy()

View file

@ -1,13 +1,44 @@
import unittest import unittest
import tkinter import tkinter
from test.support import requires, run_unittest, swap_attr from test.support import requires, run_unittest, swap_attr
from tkinter.test.support import AbstractDefaultRootTest from tkinter.test.support import AbstractDefaultRootTest, AbstractTkTest
from tkinter.commondialog import Dialog from tkinter import colorchooser
from tkinter.colorchooser import askcolor from tkinter.colorchooser import askcolor
from tkinter.commondialog import Dialog
requires('gui') 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): class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
def test_askcolor(self): def test_askcolor(self):
@ -33,7 +64,7 @@ def test_callback(dialog, master):
self.assertRaises(RuntimeError, askcolor) self.assertRaises(RuntimeError, askcolor)
tests_gui = (DefaultRootTest,) tests_gui = (ChooserTest, DefaultRootTest,)
if __name__ == "__main__": if __name__ == "__main__":
run_unittest(*tests_gui) run_unittest(*tests_gui)

View file

@ -192,6 +192,26 @@ def test_clipboard_astral(self):
with self.assertRaises(tkinter.TclError): with self.assertRaises(tkinter.TclError):
root.clipboard_get() 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): def test_event_repr_defaults(self):
e = tkinter.Event() e = tkinter.Event()
e.serial = 12345 e.serial = 12345

View file

@ -0,0 +1,2 @@
Correct call to :mod:`tkinter.colorchooser` to return RGB triplet of ints
instead of floats. Patch by Cheryl Sabella.