5

I am currently using a colorchooser from tkinter in a project to allow the user to choose a custom colour. Minimally, this can be created (in Python 3.x) through

from tkinter import colorchooser

cp = colorchooser.askcolor()

When this window appears (in Windows at least), there is a section called "Custom colors"

tkinter colorchooser window

Is there any way that I can pre-populate this section with colours of my choosing?

asongtoruin
  • 9,794
  • 3
  • 36
  • 47
  • 1
    I confirm that the colorchooser GUI is platform specific. It looks very different in linux (https://i.imgur.com/rHTIvABl.png) and there is no custom colors section. – j_4321 Feb 09 '18 at 09:40
  • 1
    @j_4321 good to know - being platform specific might make it less likely that there's an option for it. – asongtoruin Feb 09 '18 at 09:44
  • The [Tcl/Tk documentation page for the colorchooser](https://www.tcl.tk/man/tcl/TkCmd/chooseColor.htm) does not mention any platform specific options so I doubt that it is possible to set the custom colors. – j_4321 Feb 09 '18 at 09:46
  • I'd have tagged [tag:Tk] as well with this question. It's odd in a way though custom colors seem to be filled using a loop, when in paint they're initially empty(or all white). – Nae Feb 09 '18 at 13:26
  • @Nae good point, added it. – asongtoruin Feb 09 '18 at 14:09
  • 3
    @Nae, in fact, [you're right](https://github.com/NetSys/ups/blob/master/ns-allinone-2.35/tk8.5.10/win/tkWinDialog.c#L340) about a loop! @asongtoruin, such dialog is a native Windows dialog, while `tk` is a wrapper around it. It's easier to write your own wrapper for this dialog with the custom colors option. – CommonSense Feb 09 '18 at 15:03
  • @CommonSense You always know where to dig, thanks a lot for this info. – Nae Feb 09 '18 at 15:04
  • @CommonSense good knowledge! Don't suppose you have an example of how I could go about that? – asongtoruin Feb 09 '18 at 15:07

2 Answers2

3

What you see is the common Color dialog box, which is native Windows dialog. It's not possible to specify custom colors within tkinter, because they're handled at a lower level.

The naive implementation (using ctypes) could be something like this:

import ctypes
import ctypes.wintypes as wtypes


class CHOOSECOLOR(ctypes.Structure):
    """" a class to represent CWPRETSTRUCT structure
    https://msdn.microsoft.com/en-us/library/windows/desktop/ms646830(v=vs.85).aspx """

    _fields_ = [('lStructSize', wtypes.DWORD),
                ('hwndOwner', wtypes.HWND),
                ('hInstance', wtypes.HWND),
                ('rgbResult', wtypes.COLORREF),
                ('lpCustColors', ctypes.POINTER(wtypes.COLORREF)),
                ('Flags', wtypes.DWORD),
                ('lCustData', wtypes.LPARAM),
                ('lpfnHook', wtypes.LPARAM),
                ('lpTemplateName', ctypes.c_char_p)]


class ColorChooser:
    """ a class to represent Color dialog box
    https://msdn.microsoft.com/en-gb/library/windows/desktop/ms646912(v=vs.85).aspx """
    CC_SOLIDCOLOR = 0x80
    CC_FULLOPEN = 0x02
    custom_color_array = ctypes.c_uint32 * 16
    color_chooser = ctypes.windll.Comdlg32.ChooseColorW

    def to_custom_color_array(self, custom_colors):
        custom_int_colors = self.custom_color_array()

        for i in range(16):
            custom_int_colors[i] = rgb_to_int(*custom_colors[i])

        return custom_int_colors

    def askcolor(self, custom_colors):
        struct = CHOOSECOLOR()

        ctypes.memset(ctypes.byref(struct), 0, ctypes.sizeof(struct))
        struct.lStructSize = ctypes.sizeof(struct)
        struct.Flags = self.CC_SOLIDCOLOR | self.CC_FULLOPEN
        struct.lpCustColors = self.to_custom_color_array(custom_colors)

        if self.color_chooser(ctypes.byref(struct)):
            result = int_to_rgb(struct.rgbResult)
        else:
            result = None

        return result


def rgb_to_int(red, green, blue):
    return red + (green << 8) + (blue << 16)


def int_to_rgb(int_color):
    red = int_color & 255
    green = (int_color >> 8) & 255
    blue = (int_color >> 16) & 255

    return red, green, blue

colors = [(250, 0, 0), (0, 250, 0), (0, 0, 250), (255, 255, 255)] * 4

chooser = ColorChooser()
result_color = chooser.askcolor(colors)
print(result_color)

Remember, that there's a room for expansion, for example, preserving the custom color array between calls and supporting of hex colors/arbitrary array length.

CommonSense
  • 4,232
  • 2
  • 14
  • 38
1

No, there is no way to pre-fill the custom colors from tkinter.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685