Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Color a Tkinter button in multiple colors

I'm trying to put multiple colors on a single button in Tkinter. This is what I came up with:

from Tkinter import *
master = Tk()
buttons = {}

def press(clicked):
    print clicked
    return
for i in range(1,7):
    button_name = 'button '+str(i)
    buttons[i] = Button(master, text=button_name, command=lambda method=button_name:press(method))
    buttons[i].grid(row=0, column=i-1, padx=15)
    buttons[i].update()

    tot_x = buttons[i].winfo_width()
    tot_y = buttons[i].winfo_height()

    redframe = Frame(buttons[i], width=tot_x/6, background='red')
    blueframe = Frame(buttons[i], width=tot_x/6*5, background='blue')

    redframe.place(x=0, y=0, relheight=1)
    blueframe.place(relx=1./6, y=0, relheight=1)

master.mainloop()

The problem is it that it makes buttons barely clickable because frames somehow interfere with "clickability" of the buttons. And also colored frames just disappear suddenly when the cursor is placed on their position.

Is there a way to make these frames stay on the buttons and to make them not stop a click?

like image 542
DoctorEvil Avatar asked Nov 30 '25 01:11

DoctorEvil


2 Answers

Arguably, the simplest approach might be to create an image with the colors that you want, and then use the image in the button along with the text.

like image 55
Bryan Oakley Avatar answered Dec 03 '25 01:12

Bryan Oakley


Though I think Bryan's answer is simpler and fully solves the problem, you can solve the "clickability" issue with bindings (I did not experienced the second issue, so my answer is not addressing it).

The idea is to bind mouse clicks on the frames to trigger a click on the button and restore the usual button behavior. In my example below, I have bound both <ButtonPress-1> and <ButtonRelease-1> to see the button relief effects. I have also bound <Enter> and <Leave> to implement the button color change when the mouse is over it.

from Tkinter import *
master = Tk()
buttons = {}

def press(clicked):
    print clicked

def frame_enter(redframe, blueframe):
    redframe.configure(background='coral1')
    blueframe.configure(background='royalblue1')

def frame_leave(redframe, blueframe):
    redframe.configure(background='red')
    blueframe.configure(background='blue')

def on_press(event, button):
    button.event_generate('<ButtonPress-1>', x=event.x, y=event.y)

def on_release(event, button):
    button.event_generate('<ButtonRelease-1>', x=event.x, y=event.y)

for i in range(1,7):
    button_name = 'button '+str(i)
    buttons[i] = Button(master, text=button_name, command=lambda method=button_name:press(method))
    buttons[i].grid(row=0, column=i-1, padx=15)
    buttons[i].update()

    tot_x = buttons[i].winfo_width()
    tot_y = buttons[i].winfo_height()

    redframe = Frame(buttons[i], width=tot_x/6, background='red')
    blueframe = Frame(buttons[i], width=tot_x/6*5, background='blue')

    redframe.place(x=0, y=0, relheight=1)
    blueframe.place(relx=1./6, y=0, relheight=1)
    redframe.bind('<ButtonPress-1>', lambda e, b=buttons[i]: on_press(e, b)) 
    blueframe.bind('<ButtonPress-1>', lambda e, b=buttons[i]: on_press(e, b)) 
    redframe.bind('<ButtonRelease-1>', lambda e, b=buttons[i]: on_release(e, b)) 
    blueframe.bind('<ButtonRelease-1>', lambda e, b=buttons[i]: on_release(e, b)) 
    redframe.bind('<Enter>', lambda e, rf=redframe, bf=blueframe: frame_enter(rf, bf)) 
    blueframe.bind('<Enter>', lambda e, rf=redframe, bf=blueframe: frame_enter(rf, bf)) 
    redframe.bind('<Leave>', lambda e, rf=redframe, bf=blueframe: frame_leave(rf, bf)) 
    blueframe.bind('<Leave>', lambda e, rf=redframe, bf=blueframe: frame_leave(rf, bf)) 

master.mainloop()

Edit: Keeping the buttons is not necessary, they can be replaced by frames. This way, no need to use place, the colored frames can just be packed inside:

from Tkinter import *
master = Tk()
buttons = {}

def press(clicked):
    print clicked

def frame_enter(redframe, blueframe):
    redframe.configure(background='coral1')
    blueframe.configure(background='royalblue1')

def frame_leave(redframe, blueframe):
    redframe.configure(background='red')
    blueframe.configure(background='blue')

def on_press(event, widget):
    widget.configure(relief='sunken')  # mimic button's relief effect on click 

def on_release(event, widget, text):
    widget.configure(relief='raised')  # mimic button's relief effect on click 
    press(text)

for i in range(1,7):
    button_name = 'button '+ str(i)
    buttons[i] = Frame(master, relief='raised', width=78, height=24, bd=2)
    buttons[i].grid(row=0, column=i-1, padx=15)
    buttons[i].update_idletasks()

    tot_x = buttons[i].winfo_width()
    tot_y = buttons[i].winfo_height()

    redframe = Frame(buttons[i], width=tot_x/6, height=tot_y, background='red')
    blueframe = Frame(buttons[i], width=tot_x/6*5, height=tot_y, background='blue')

    redframe.pack(side='left')
    blueframe.pack(side='left')
    redframe.bind('<ButtonPress-1>', lambda e, b=buttons[i]: on_press(e, b)) 
    blueframe.bind('<ButtonPress-1>', lambda e, b=buttons[i]: on_press(e, b)) 
    redframe.bind('<ButtonRelease-1>', lambda e, b=buttons[i], t=button_name: on_release(e, b, t)) 
    blueframe.bind('<ButtonRelease-1>', lambda e, b=buttons[i], t=button_name: on_release(e, b, t)) 
    redframe.bind('<Enter>', lambda e, rf=redframe, bf=blueframe: frame_enter(rf, bf)) 
    blueframe.bind('<Enter>', lambda e, rf=redframe, bf=blueframe: frame_enter(rf, bf)) 
    redframe.bind('<Leave>', lambda e, rf=redframe, bf=blueframe: frame_leave(rf, bf)) 
    blueframe.bind('<Leave>', lambda e, rf=redframe, bf=blueframe: frame_leave(rf, bf)) 
like image 42
j_4321 Avatar answered Dec 03 '25 00:12

j_4321



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!