Tobias
Tobias

Reputation: 176

Python: How to change the border color of ttk.Checkbutton when it is active?

I want to change the bordercolor of the ttk.Checkbutton widget, which is a light blue by default. I tried to use the configure function of ttk.Style() and managed to change the color of the whole button or other things when it is active, but not the border of the Checkbox. Additional question is where I could find this kind of information in for example some kind of documentation of all options that can be changed in the ttk style?

Here is an example of what I tried:

from tkinter import ttk

root = Tk()

s = ttk.Style()
s.configure('MyOwn.TCheckbutton',
    background='white',)
s.map('MyOwn.TCheckbutton',
       foreground=[('active', 'red')])

cb = ttk.Checkbutton(root,text='Text',style='MyOwn.TCheckbutton')
cb.pack()
root.mainloop()

I can change the color of the text when i hover over the checkbutton, but I don't find how to change the border color of the Checkbox.

Upvotes: 3

Views: 3758

Answers (2)

jimbob88
jimbob88

Reputation: 727

Your Issue

I believe the issue you are having is the fact the Ttk Checkbutton is not in fact a drawn widget but is in fact an image.

How does one find out?

If one takes a look at the github project ttkthemes and takes a look at the themes folder, and goes to, for example, the radiance folder (this is an UBUNTU like theme) and then to the folder inside that labelled "radiance".

One will see something like this: gifs We see a folder filled with "GIFs", these are image files, if one scrolls down and finds the files:

check-dc.gif (Disabled Deselected)
check-du.gif (Disabled Deselected)
check-nc.gif (Normal Selected)
check-nu.gif (Normal Deselected)

As we can see if we look through these files these are all the different possible states of a checkbutton.

Under windows these checkbuttons would look like so (generated with this code):

checkbuttons

How does one include custom checkbuttons?

A great post about this can be found here, where they create a custom checkbutton by adding a green and a red square to a canvas, but what if one wants to make something similiar to what you want. Where you change the colour of the checkbutton.

How does one change the border colour of a default checkbutton?

Firstly let's convert all of the Checkbutton images into the gif format, I have done this for you and they are available to download from my Google Drive or my Gofile.

From my Google Drive you will need to fetch:

check-nc.gif (Normal Selected)
check-nu.gif (Normal Deselected)

as you don't need to worry about the deactivated values because they will never be focused.

Now lets look at how they load the images in the afforementioned post:

on_image = tk.PhotoImage(width=48, height=24)
off_image = tk.PhotoImage(width=48, height=24)
on_image.put(("green",), to=(0, 0, 23,23))
off_image.put(("red",), to=(24, 0, 47, 23))

Here they create two blank images, which are 48px X 24px, and two one of them they add a green square on the left hand side, and the other they add one to the right hand side.

Our approach to this is going to be similiar but slightly different, instead we are going to load the two gif files you downloaded and then we will write a script that will make 4 rectangles that'll cover the currently gray outline of the checkbutton. Like so:

import tkinter as tk

root = tk.Tk()

def edit_check(colour, image):
    image.put((colour,), to=(0, 0, 1, 13)) # LEFT
    image.put((colour,), to=(0, 0, 13, 1)) # TOP
    image.put((colour,), to=(12, 0, 13, 13)) # RIGHT
    image.put((colour,), to=(0, 12, 13, 13)) # BOTTOM

def focus_in(event):
    print(event)
    image = off_image if var1.get() == 0 else on_image
    edit_check("red", image)
    cb1.configure(image=image)
    cb1.image = image

def focus_out(event):
    print(event)
    image = off_image if var1.get() == 0 else on_image
    edit_check("black", image)
    cb1.configure(image=image)
    cb1.image = image

on_image = tk.PhotoImage(file="check-nc.gif") # Instead of creating a new image we open an existing one
off_image = tk.PhotoImage(file="check-nu.gif") # Instead of creating a new image we open an existing one

var1 = tk.IntVar(value=0)
cb1 = tk.Checkbutton(root, image=off_image, selectimage=on_image, indicatoron=False,
                     onvalue=1, offvalue=0, variable=var1, offrelief='sunken')
cb1.pack(padx=20, pady=10)

# THESE IF YOU WANT TRUE FOCUS
#cb1.bind("<FocusIn>", focus_in)
#cb1.bind("<FocusOut>", focus_out)

# THESE IF YOU WANT MOUSE OVER
cb1.bind("<Enter>", focus_in)
cb1.bind("<Leave>", focus_out)
root.mainloop()

Improving the code with Object Orientated Programming

This here is the much better system for your use case (note it doesn't require any image files as the files are saved as base64 strings)

import tkinter as tk

check_nu = b'iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAYAAABy6+R8AAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TRZEWB4uIOGSoTi2IijpKFYtgobQVWnUwufQLmjQkKS6OgmvBwY/FqoOLs64OroIg+AHi4uqk6CIl/i8ptIjx4Lgf7+497t4BQqPCVLNrHFA1y0jFY2I2tyr2vCKAIAYRwYzETD2RXszAc3zdw8fXuyjP8j735wgqeZMBPpF4jumGRbxBPL1p6Zz3iUOsJCnE58QRgy5I/Mh12eU3zkWHBZ4ZMjKpeeIQsVjsYLmDWclQiaeIw4qqUb6QdVnhvMVZrdRY6578hYG8tpLmOs0RxLGEBJIQIaOGMiqwEKVVI8VEivZjHv5hx58kl0yuMhg5FlCFCsnxg//B727NwuSEmxSIAd0vtv0xCvTsAs26bX8f23bzBPA/A1da219tALOfpNfbWvgI6N8GLq7bmrwHXO4AQ0+6ZEiO5KcpFArA+xl9Uw4YuAX61tzeWvs4fQAy1NXyDXBwCIwVKXvd4929nb39e6bV3w/0UXLbKEvbjQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB+MMDRctIGmzOYIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAALElEQVQoz2M0Njb+z0AiYGFgYGA4c+YMI7EaTExM/jMxkAFGNQ1jTYzkpD0ATtMHS/nRiQwAAAAASUVORK5CYII='
check_nc = b'iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAYAAABy6+R8AAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TRZEWB4uIOGSoTi2IijpKFYtgobQVWnUwufQLmjQkKS6OgmvBwY/FqoOLs64OroIg+AHi4uqk6CIl/i8ptIjx4Lgf7+497t4BQqPCVLNrHFA1y0jFY2I2tyr2vCKAIAYRwYzETD2RXszAc3zdw8fXuyjP8j735wgqeZMBPpF4jumGRbxBPL1p6Zz3iUOsJCnE58QRgy5I/Mh12eU3zkWHBZ4ZMjKpeeIQsVjsYLmDWclQiaeIw4qqUb6QdVnhvMVZrdRY6578hYG8tpLmOs0RxLGEBJIQIaOGMiqwEKVVI8VEivZjHv5hx58kl0yuMhg5FlCFCsnxg//B727NwuSEmxSIAd0vtv0xCvTsAs26bX8f23bzBPA/A1da219tALOfpNfbWvgI6N8GLq7bmrwHXO4AQ0+6ZEiO5KcpFArA+xl9Uw4YuAX61tzeWvs4fQAy1NXyDXBwCIwVKXvd4929nb39e6bV3w/0UXLbKEvbjQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB+MMDRctDrVlNE0AAAAjdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVAgd2l0aCBsb3ZlyGW0XgAAAJdJREFUKM+d0rENAyEMBdDvKA1zeART07MIGzAFE7EDK1B5CMqf7pIUlyP3JZdP+rItZkb8mScAjDFkF8QY+cCNbCF3R86ZvXduIXdHKYWqipSSXKJP0FqTEMK7Xu+dOWe6+yU4UEpJVBWlFLr7TwAAYmYcY8haC7VWzjkBAGfga+UhBLTWRFVPwREzI0nsjpndO67c+b0XBDxvkWRMW24AAAAASUVORK5CYII='


class example_window:
    def __init__(self, master):
        self.master = master
        chckbox = custom_checkbox(master, "red")
        chckbox.pack(padx=20, pady=10)
        

class custom_checkbox(tk.Checkbutton):
    def __init__(self, parent, colour, *args, **kwargs):
        default_kwargs = {'image': None, 'selectimage': None, 'indicatoron': False,
                          'onvalue': 1, 'offvalue': 0, 'variable': None, 'offrelief': 'sunken'}
        for key, value in default_kwargs.items():
            if key not in kwargs:
                kwargs[key] = value

        self.colour = colour
        self.curr_colour = "black"
        self.hover = False
        if kwargs['variable'] is None:
            self.variable = kwargs['variable'] = tk.IntVar(value=1)
        else:
            self.variable = kwargs['variable']
        print(kwargs, kwargs['variable'].get())
        if kwargs['variable'].get() == 0:
            if kwargs['image'] is None:
                self.off_image = kwargs['image'] = tk.PhotoImage(data=check_nu)
            else:
                self.off_image = kwargs['image']
            if kwargs['selectimage'] is None:
                self.on_image = tk.PhotoImage(data=check_nc)
            else:
                self.on_image = kwargs['selectimage']
        else:
            if kwargs['image'] is None:
                self.on_image = kwargs['image'] = tk.PhotoImage(data=check_nc)
            else:
                self.on_image = kwargs['image']
            if kwargs['selectimage'] is None:
                self.off_image = tk.PhotoImage(data=check_nu)
            else:
                self.off_image = kwargs['selectimage']

        tk.Checkbutton.__init__(self, parent, *args, **kwargs)
        self.bind("<Enter>", self.focus_in)
        self.bind("<Leave>", self.focus_out)
        self.variable.trace("w", self.focus_update)

    def edit_check(self, colour, image):
        image.put((colour,), to=(0, 0, 1, 13)) # LEFT
        image.put((colour,), to=(0, 0, 13, 1)) # TOP
        image.put((colour,), to=(12, 0, 13, 13)) # RIGHT
        image.put((colour,), to=(0, 12, 13, 13)) # BOTTOM

        
    def focus_in(self, event=None):
        image = self.on_image if self.variable.get() == self['onvalue'] else self.off_image
        self.edit_check(self.colour, image)
        self.curr_colour = self.colour
        self.configure(image=image)
        self.image = image
        self.hover = True

    def focus_out(self, event=None):
        image = self.on_image if self.variable.get() == self['onvalue'] else self.off_image
        self.edit_check("black", image)
        self.curr_colour = "black"
        self.configure(image=image)
        self.image = image
        self.hover = False

    def focus_update(self, *args):

        if self.variable.get() == self['onvalue']:
            image = self.on_image
        else:
            image = self.off_image
        self.configure(image=image)
        self.image = image
        if self.hover:
            self.focus_in()

def main():
    root = tk.Tk()
    example_window_gui = example_window(root)
    root.mainloop()

if __name__ == '__main__':
    main()

If you want to be able to add text to this widget, check out this code

Upvotes: 4

Related Questions