Reputation: 15226
According to the accepted answer on this post the use of .configure(highlightbackground='red')
on a button should apply a color around the button however in testing I cannot reproduce what the poster has demonstrated in their gif recording.
Here is my test case: (Note even copy pasting the posters code I cannot get the highlight effect they are showing)
import tkinter as tk
root = tk.Tk()
btn = tk.Button(root, text='test', bg="#000000", fg="#ffffff", highlightthickness=4, activebackground="#ffffff",
activeforeground="#000000", highlightbackground='red', highlightcolor='red')
btn.pack()
btn.focus_set()
root.mainloop()
Resulting app:
With some extensive searching I am not finding much on highlightbackground
in the way of Q/A about the same issue so maybe something is missing. I have also tried to set the focus as this documentation states the widget needs focus with the same non result.
Maybe it could be version or OS related...
OS - Windows 10 Pro
Python - 3.6.2
Updated example using Krrr's post. So this does kinda work now however the issue at hand here is that it is resizing the button and not providing the correct highlighted color.
import tkinter as tk
def ResponsiveWidget(widget, *args, **kwargs):
bindings = {
'<FocusIn>': {'highlightbackground': 'red', 'highlightcolor':'red'},
'<FocusOut>': {'highlightbackground': '#d9d9d9', 'highlightcolor':'SystemButtonFace'},
'<Enter>': {'state': 'active'},
'<Leave>': {'state': 'normal'}
}
for k, v in bindings.items():
root.bind_class('Button', k, lambda e, kwarg=v: e.widget.config(**kwarg))
def update_active(event):
global previous_button
if previous_button != event.widget:
previous_button.config(default='normal')
event.widget.config(default='active')
previous_button = event.widget
root = tk.Tk()
button_list = []
previous_button = None
for i in range(5):
if i == 0:
button_list.append(tk.Button(root, text='test', bg="#000000", fg="#ffffff", highlightthickness=5,
activebackground="#ffffff", activeforeground="#000000", default='active'))
previous_button = button_list[-1]
else:
button_list.append(tk.Button(root, text='test', bg="#000000", fg="#ffffff", highlightthickness=5,
activebackground="#ffffff", activeforeground="#000000", default='normal'))
button_list[-1].pack(padx=5, pady=5)
button_list[-1].bind('<ButtonRelease-1>', update_active)
root.mainloop()
Results:
Expectation:
Upvotes: 2
Views: 1743
Reputation: 486
Thank you for your question, your code is good and to solve your problem just use default="active"
import tkinter as tk
root = tk.Tk()
btn = tk.Button(root, text='test', bg="#000000", fg="#ffffff", highlightthickness=4,
activebackground="#ffffff",
activeforeground="#000000", highlightbackground='red',
default="active", highlightcolor='red')
btn.pack()
btn.focus_set()
root.mainloop()
Upvotes: 0
Reputation: 13888
Unfortunately it seems Windows OS doesn't seem to be triggering the state
and default
widget configs properly. However this is achievable by doing your own bindings.
If you only have a handful of widgets that need this behaviour, you can have create a widget wrapper:
def ResponsiveWidget(widget, *args, **kwargs):
bindings = {
'<FocusIn>': {'default':'active'}, # for Keyboard focus
'<FocusOut>': {'default': 'normal'},
'<Enter>': {'state': 'active'}, # for Mouse focus
'<Leave>': {'state': 'normal'}
}
# Create the widget instance
w = widget(*args, **kwargs)
# Set the bindings for the widget instance
for k, v in bindings.items():
w.bind(k, lambda e, kwarg=v: e.widget.config(**kwarg))
# Remember to return the created and binded widget
return w
btn = ResponsiveWidget(tk.Button, root, text='test3', bg="#000000", fg="#ffffff", highlightthickness=10, activebackground="#ffffff",
activeforeground="#000000", highlightbackground='red', highlightcolor='green')
btn2 = ResponsiveWidget(tk.Button, root, text='test4', bg="#000000", fg="#ffffff", highlightthickness=10, activebackground="#ffffff",
activeforeground="#000000", highlightbackground='green', highlightcolor='red')
On the other hand, if you wanted the entire class of the widget to always trigger the default/state properly, you can use bind_class
instead:
bindings = {
'<FocusIn>': {'default':'active'}, # for Keyboard focus
'<FocusOut>': {'default': 'normal'},
'<Enter>': {'state': 'active'}, # for Mouse focus
'<Leave>': {'state': 'normal'}
}
for k, v in bindings.items():
root.bind_class('Button', k, lambda e, kwarg=v: e.widget.config(**kwarg))
This seem to trigger the events just fine.
If you just want to replicate the functionality of the highlight colour, a less desirable method would be to change the highlightcolor
config on focus instead:
bindings = {
'<FocusIn>': {'highlightcolor':'red'},
'<FocusOut>': {'highlightcolor': 'SystemButtonFace'},
'<Enter>': {'state': 'active'},
'<Leave>': {'state': 'normal'}
}
for k, v in bindings.items():
root.bind_class('Button', k, lambda e, kwarg=v: e.widget.config(**kwarg))
# Note this method requires you to set the default='active' for your buttons
btn = tk.Button(root, text='test', bg="#000000", fg="#ffffff", highlightthickness=10, activebackground="#ffffff",
activeforeground="#000000", highlightcolor='SystemButtonFace', default='active')
# ...
I'd consider this more a hacky method.
Edit: For completeness, here's a MCVE using bind_class
:
import tkinter as tk
root = tk.Tk()
bindings = {
'<FocusIn>': {'highlightcolor':'red'},
'<FocusOut>': {'highlightcolor': 'SystemButtonFace'},
'<Enter>': {'state': 'active'},
'<Leave>': {'state': 'normal'}
}
for k, v in bindings.items():
root.bind_class('Button', k, lambda e, kwarg=v: e.widget.config(**kwarg))
btns = list(range(5))
for btn in btns:
btns[btn] = tk.Button(root, text='test', bg="#000000", fg="#ffffff", highlightthickness=5, activebackground="#ffffff",
activeforeground="#000000", highlightcolor='SystemButtonFace', default='active', padx=5, pady=5)
btns[btn].pack()
btns[0].focus_set()
root.mainloop()
And MCVE using ResponsiveWidget
function:
import tkinter as tk
root = tk.Tk()
def ResponsiveWidget(widget, *args, **kwargs):
bindings = {
'<FocusIn>': {'highlightcolor':'red'}, # for Keyboard focus
'<FocusOut>': {'highlightcolor': 'SystemButtonFace'},
'<Enter>': {'state': 'active'}, # for Mouse focus
'<Leave>': {'state': 'normal'}
}
# Create the widget instance
w = widget(*args, **kwargs)
# Set the bindings for the widget instance
for k, v in bindings.items():
w.bind(k, lambda e, kwarg=v: e.widget.config(**kwarg))
# Remember to return the created and binded widget
return w
btns = list(range(5))
for btn in btns:
btns[btn] = ResponsiveWidget(tk.Button, root, text=f'test{btn}', bg="#000000", fg="#ffffff", highlightthickness=10, activebackground="#ffffff",
activeforeground="#000000", highlightcolor='SystemButtonFace', default='active', padx=5, pady=5)
btns[btn].pack()
btns[0].focus_set()
root.mainloop()
Upvotes: 3