让 win32 api 颜色选择器总是在最前面?

Make win32 api color chooser always on top?

我将按照此 在 tkinter 中包含一个颜色选择器,它会记住上次选择的颜色和自定义颜色。它按预期工作,但颜色选择器 window 始终创建在左上角。我想选择颜色选择器的初始位置,并将其设置为始终在最前面。有什么方法可以实现吗?

这是我目前的情况:

import ctypes
import ctypes.wintypes as wtypes


"""

thanks!
"""


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
    CC_RGBINIT = 0x01
    WS_EX_TOPMOST = 0x08
    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])
                if i < len(custom_colors)
                else rgb_to_int(*(255, 255, 255))
            )

        return custom_int_colors

    def askcolor(self, initialColor, 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 | self.CC_RGBINIT
        struct.lpCustColors = self.to_custom_color_array(custom_colors)
        struct.rgbResult = rgb_to_int(*initialColor)

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

        return result, struct.lpCustColors


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

可以在WM_INITDIALOG时使用lpfnHook to control the generation of the dialog box, and then call SetWindowPos调整生成对话框的位置

一些 C++ 代码:

UINT_PTR CALLBACK Lpcchookproc(
    HWND hWnd,
    UINT message,
    WPARAM wParam,
    LPARAM lParam
)

{
    if (message == WM_INITDIALOG)
    {
        SetWindowPos(hWnd, HWND_TOPMOST, 400, 400, 0, 0, SWP_NOSIZE);
    }
    return 0;
}

int main()
{
    CHOOSECOLOR cc;                 // common dialog box structure 
    static COLORREF acrCustClr[16]; // array of custom colors 
    HBRUSH hbrush;                  // brush handle
    static DWORD rgbCurrent;        // initial color selection
    // Initialize CHOOSECOLOR 
    ZeroMemory(&cc, sizeof(cc));
    cc.lStructSize = sizeof(cc);
    cc.lpCustColors = (LPDWORD)acrCustClr;
    cc.Flags = CC_FULLOPEN | CC_RGBINIT | CC_ENABLEHOOK;
    cc.lpfnHook = Lpcchookproc;
    ChooseColor(&cc) == TRUE;
    return 0;
}

感谢所有回复的人,这是我最终使用的。


import ctypes
import ctypes.wintypes as wtypes


"""


a color chooser for tkinter with initial color, and custom color settings.
"""


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
    CC_RGBINIT = 0x01
    WS_EX_TOPMOST = 0x08
    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])
                if i < len(custom_colors)
                else rgb_to_int(*(255, 255, 255))
            )

        return custom_int_colors

    def askcolor(self, hostId, initialColor, custom_colors):
        #hostId is .winfo_id() of parent window, colors are hex rgb string, and 
        #custom_colors accepts up to 16 hex strings.
        struct = CHOOSECOLOR()
        ctypes.memset(ctypes.byref(struct), 0, ctypes.sizeof(struct))
        struct.lStructSize = ctypes.sizeof(struct)
        struct.Flags = self.CC_SOLIDCOLOR | self.CC_FULLOPEN | self.CC_RGBINIT
        struct.lpCustColors = self.to_custom_color_array(custom_colors)
        struct.rgbResult = rgb_to_int(*initialColor)
        struct.hwndOwner = hostId
        if self.color_chooser(ctypes.byref(struct)):
            result = int_to_rgb(struct.rgbResult)
        else:
            result = None

        return result, [int_to_rgb(struct.lpCustColors[i]) for i in range(16)]


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