I have a row of widgets which contains a ttk.Combobox and I want to change the background colour of the widgets in the row when I tick a Checkbutton at the end of the row. With tkinter it is simple just to use configure but with ttk you have to use a theme which seems to be neither dynamic nor specific to a single widget. Is there a way to achieve this functionality ?
Thankyou.
in response to fhdrsdg's comment. I can't get it working but this code demonstrates it
import Tkinter as tk
import ttk
def skillUsed():
if chkUsedVar.get() == 1:
style.map('TCombobox', background=[('readonly','green')])
style.map('TCombobox', foreground=[('readonly','red')])
else:
style.map('TCombobox', background=[('readonly','white')])
style.map('TCombobox', foreground=[('readonly','black')])
root = tk.Tk()
style = ttk.Style()
cboxVar1 = tk.StringVar()
cboxVar1.set("spam")
cboxVar2 = tk.StringVar()
cboxVar2.set("silly")
chkUsedVar = tk.IntVar()
chk = tk.Checkbutton(root, text='Used', variable=chkUsedVar, command=skillUsed)
chk.grid(row=0, column=2)
combo01 = ttk.Combobox(root, values=['spam', 'eric', 'moose'], textvariable=cboxVar1)
combo01['state'] = 'readonly'
combo01.grid(row=0, column=0)
combo02 = ttk.Combobox(root, values=['parrot', 'silly', 'walk'], textvariable=cboxVar2)
combo02['state'] = 'readonly'
combo02.grid(row=0, column=1)
root.mainloop()
When the tick box is clicked the foreground goes red and when unticked it goes black. The issue is the background never changes (but doesn't error) and the style is applied globally to both comboboxes and I want to apply it to a single box.
I have a workaround which I will use just using tkinter's OptionMenu and everything I can find on the tinterweb implies it can't be done with ttk widgets but that seems a bit of a limit to ttk widgets but I have little to no experience with tkinter or ttk.
the workaround is :-
from Tkinter import *
def skillUsed():
if chkUsedVar.get() == 1:
opt01.configure(bg="#000fff000")
opt01.configure(highlightbackground="#000fff000")
opt01.configure(activebackground="#000fff000")
opt01.configure(highlightcolor="#000fff000")
opt01["menu"].configure(bg="#000fff000")
else:
opt01.configure(bg=orgOptbg)
opt01.configure(highlightbackground=orgOpthighlightbackground)
opt01.configure(activebackground=orgOptactivebackground)
opt01.configure(highlightcolor=orgOpthighlightcolor)
opt01["menu"].configure(bg=orgOptmenu)
root = Tk()
optionList = ('parrot','silly','walk')
varopt01 = StringVar()
varopt01.set(optionList[0])
chkUsedVar = IntVar()
opt01 = OptionMenu(root, varopt01, *optionList)
opt01.grid(row=0, column=0)
orgOptbg = opt01.cget("bg")
orgOpthighlightbackground = opt01.cget("highlightbackground")
orgOptactivebackground = opt01.cget("activebackground")
orgOpthighlightcolor = opt01.cget("highlightcolor")
orgOptmenu = opt01["menu"].cget("bg")
chk = Checkbutton(root, text='Used', variable=chkUsedVar, command=skillUsed)
chk.grid(row=0, column=1)
root.mainloop()
Thankyou
First, if you want to apply the style to a single combobox, give it a name like 'custom.TCombobox' so that it inherits from 'TCombobox' but doesn't change the default combobox style. Then all you have to do is set the style of your combobox to 'custom.TCombobox'.
Secondly, the background was not changing because it's the fieldbackground you want to change.
EDIT: What can be customized in a style depends on the ttk theme being used. For instance, the default Mac and Windows themes don't allow much customization and the fieldbackground color of the combobox cannot be changed. However, the 'alt' and 'clam' themes allow more customization.
Here is an example based on your code:
import tkinter as tk
from tkinter import ttk
def skillUsed():
if chkUsedVar.get() == 1:
style.map('custom.TCombobox', fieldbackground=[('readonly','green')])
style.map('custom.TCombobox', foreground=[('readonly','red')])
else:
style.map('custom.TCombobox', fieldbackground=[('readonly','white')])
style.map('custom.TCombobox', foreground=[('readonly','black')])
root = tk.Tk()
style = ttk.Style()
style.theme_use('alt')
cboxVar1 = tk.StringVar()
cboxVar1.set("spam")
cboxVar2 = tk.StringVar()
cboxVar2.set("silly")
chkUsedVar = tk.IntVar()
chk = tk.Checkbutton(root, text='Used', variable=chkUsedVar, command=skillUsed)
chk.grid(row=0, column=2)
combo01 = ttk.Combobox(root, values=['spam', 'eric', 'moose'], textvariable=cboxVar1)
combo01['state'] = 'readonly'
combo01.grid(row=0, column=0)
combo02 = ttk.Combobox(root, values=['parrot', 'silly', 'walk'], textvariable=cboxVar2, style='custom.TCombobox')
combo02['state'] = 'readonly'
combo02.grid(row=0, column=1)
root.mainloop()
Related
from tkinter import *
import tkinter.ttk as ttk
root = Tk()
root.title("TEST ")
lab_pro_val4=Label(root)
lab_pro_val4=Label(root, text="Anything input", width=30, height=1 )
lab_pro_val4.pack()
ent_pro_val4 = Entry(root)
ent_pro_val4.pack()
def btncmd4_add():
tru = (ent_pro_val4=='TEST123')
print(tru)
btn4_ppid = Button(root, text="Check ", command= btncmd4_add,bg = "white")
btn4_ppid.pack()
root.mainloop()
I used Tkinter but I had some trouble.
My question : why is it diff from entry to str(same)
I type the 'TEST123' in entry box.
But it's False... Why is it different?
Please let me know.
ent_pro_val4 is Entry widget, you need to get value of said widget, which can be done using .get(), that is replace
tru = (ent_pro_val4=='TEST123')
using
tru = (ent_pro_val4.get()=='TEST123')
I have developped a app in Python (tested with 3.8 and 3.9 on Windows 10) with Tkinter. I am using a Combobox and a Treeview. I want to change dynamically the width of dropdown listbox and I could do it by changing the style of TCombobox with the parameter postoffset.
However, everytime I click on the Combobox, the table next to it moves and extends its width by itself. I really don't know where this problem comes from.
I have created a simple code so that you can reproduce the problem.
import tkinter as tk
from tkinter import ttk
import tkinter.font as tkfont
def combo_configure(event, style):
combo = event.widget
long = max(combo.cget('values'), key=len)
font = tkfont.Font(family="Helvetica", size=10)
width = max(0,font.measure(long.strip() + '0') - combo.winfo_width())
style.configure('TCombobox', postoffset=(0,0,width,0))
win = tk.Tk()
win.geometry("850x250")
style = ttk.Style()
f = tk.Frame(win)
f.configure(bg="black")
f.pack()
combo = ttk.Combobox(f, style="TCombobox", values=["It's a test on Combobox style."], width=10)
combo.bind('<ButtonPress>', lambda e : combo_configure(e, style))
combo.pack(side="left")
tree = ttk.Treeview(f, column=["Column 1"], show='headings', height=10)
tree.heading(0, text="Column 1")
tree.column(0, anchor=tk.CENTER, width=125)
tree.pack()
win.mainloop()
Thanks.
hy I am working on tkinter python project.
I am trying to position text to the Left inside OptionMenu using anchor option but it does not seem to work. I am using ttk theme widgets.
Here is the code that I am trying currently.
s = ttk.Style()
s.configure('my.TMenubutton', font=("Cambria", fontSize, "bold"), background="white", anchor = W )
shapeMenu = ttk.OptionMenu(shapeFrame, shape, myShapes[1], *myShapes, style='my.TMenubutton', command=getShapes)
What I am doing wrong ?
It seems like there is no anchor option under TMenubutton. I tried TLabel, and it worked. Don't know if there is any side effect though.
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.geometry('300x100')
CHART_TYPES = ('Reticle', 'Circle Grid', 'Checkerboard', 'Grille', 'Slanted Edge MTF')
s = ttk.Style()
s.configure('my.TLabel', font=("Cambria", 10, "bold"), background="white", anchor = 'w' )
chart_type = tk.StringVar()
chart_type.set(CHART_TYPES[0])
# chart_selector = tk.OptionMenu(root, chart_type, *CHART_TYPES)
chart_selector = ttk.OptionMenu(root, chart_type, *CHART_TYPES, style='my.TLabel')
# chart_selector.configure(anchor='w')
chart_selector.pack(expand=True, fill='x')
root.mainloop()
I have some code (as shown below) which prompts the user to select which colour to change the GUI to. But my problem is that it only changes the background. I'd like to know if there's a way to change the background of every label and button at once or do I have to change each label/button individually.
import tkinter
window = tkinter.Tk()
colour_frame = tkinter.Frame(window)
options_frame = tkinter.Frame(window)
def colours():
options_frame.pack_forget()
red.pack()
orange.pack()
back_button.pack()
colour_frame.pack()
def back():
options_frame.pack()
colour_frame.pack_forget()
def make_red():
window.configure(background="red")
def make_orange():
window.configure(background="orange")
colour_button = tkinter.Button(options_frame, text="Appearance", command=colours)
red = tkinter.Button(colour_frame, text="RED", command=make_red)
red.configure(bg = "red")
orange = tkinter.Button(colour_frame, text="ORANGE", command=make_orange)
orange.configure(bg = "orange")
back_button = tkinter.Button(colour_frame, text="Back", command=back)
window.mainloop()
You can make a list containing all your widgets you want to change
myWidgets = [button1, label1, ... ] # List of widgets to change colour
for wid in myWidgets:
wid.configure(bg = newColour)
Here's an example code of changing the background colour of multiple labels at once.
import tkinter as tk
# Change all label backgrounds
def change_colour():
c = user.get() #Get the entered text of the Entry widget
for wid in widget_list:
wid.configure(bg = c)
# Create GUI
root = tk.Tk()
tk.Label(root, text='Enter a colour').pack()
user = tk.Entry(root)
user.pack()
label_frame = tk.Frame(root)
label_frame.pack()
btn = tk.Button(root, text='Change Colour', command = change_colour)
btn.pack()
widget_list = [user, btn] # Add defined widgets to list
#Dynamicly create labels for example
for x in range(10):
lbl = tk.Label(label_frame, text='Label '+str(x))
lbl.pack(side = tk.LEFT)
widget_list.append(lbl) #Add widget object to list
root.mainloop()
Or if you have a Frame already containing all the widgets you want to change, then you can use this instead.
parent_widget.winfo_children() will return a list containing all the widgets stored inside the parent widget
def change_colour():
c = user.get()
for wid in label_frame.winfo_children():
wid.configure(bg = c)
Try using ttk for some of your GUI elements. ttk allows you to create styles for widgets and update the style to all widgets at once (at least for those that have the same style). You may need to mix the usage of ttk and tkinter, but it should make things a bit easier in the long run. Here is an example I made:
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
# Creating a style for the buttons
color_style_button = ttk.Style()
color_style_button.configure("color.TButton", foreground="red")
def change_color(color):
# This function changes the style to all buttons using the "color.Button style"
if color == "red":
color_style_button.configure("color.TButton", foreground="red")
elif color == "blue":
color_style_button.configure("color.TButton", foreground="blue")
elif color == "green":
color_style_button.configure("color.TButton", foreground="green")
frame_a = ttk.Frame(root)
frame_a.pack()
red_button = ttk.Button(frame_a, text="Red", command=lambda: change_color("red"), style="color.TButton")
red_button.pack()
blue_button = ttk.Button(frame_a, text="Blue", command=lambda: change_color("blue"), style="color.TButton")
blue_button.pack()
green_button = ttk.Button(frame_a, text="Blue", command=lambda: change_color("green"), style="color.TButton")
green_button.pack()
root.mainloop()
I recommend checking out this site to learn more about ttk and styles.
I wrote a small farad converter to learn GUI programming. It works great, looks fine-ish. The only problem is I can't seem to figure out how to control this strange highlighting that comes up on my ttk.Combobox selections. I did use a ttk.Style(), but it only changed the colors of the ttk.Combobox background, entries, etc. I also tried changing openbox/gtk themes.
I'm talking about what's seen there on the text "microfarads (uF)".
It'd be fine, if it highlighted the entire box; but I'd rather have it gone completely.
How can I manipulate a ttk.Combobox's selection highlight?
# what the farad?
# thomas kirkpatrick (jtkiv)
from tkinter import *
from tkinter import ttk
# ze la programma.
def conversion(*args):
# this is the numerical value
inV = float(inValue.get())
# these two are the unit (farads, microfarads, etc.) values
inU = inUnitsValue.current()
outU = outUnitsValue.current()
# "mltplr" is multiplied times inValue (inV)
if inU == outU:
mltplr = 1
else:
mltplr = 10**((outU - inU)*3)
outValue.set(inV*mltplr)
# start of GUI code
root = Tk()
root.title("What the Farad?")
# frame
mainFrame = ttk.Frame(root, width="364", padding="4 4 8 8")
mainFrame.grid(column=0, row=0)
# input entry
inValue = StringVar()
inValueEntry = ttk.Entry(mainFrame, width="20", justify="right", textvariable=inValue)
inValueEntry.grid(column=1, row=1, sticky="W")
# input unit combobox
inUnitsValue = ttk.Combobox(mainFrame)
inUnitsValue['values'] = ('kilofarads (kF)', 'farads (F)', 'millifarads (mF)', 'microfarads (uF)', 'nanofarads (nF)', 'picofarads (pF)')
inUnitsValue.grid(column=2, row=1, sticky="e")
inUnitsValue.state(['readonly'])
inUnitsValue.bind('<<ComboboxSelected>>', conversion)
# result label
outValue = StringVar()
resultLabel = ttk.Label(mainFrame, textvariable=outValue)
resultLabel.grid(column=1, row=2, sticky="e")
# output unit combobox
outUnitsValue = ttk.Combobox(mainFrame)
outUnitsValue['values'] = ('kilofarads (kF)', 'farads (F)', 'millifarads (mF)', 'microfarads (uF)', 'nanofarads (nF)', 'picofarads (pF)')
outUnitsValue.grid(column=2, row=2, sticky="e")
outUnitsValue.state(['readonly'])
outUnitsValue.bind('<<ComboboxSelected>>', conversion)
# padding for widgets
for child in mainFrame.winfo_children(): child.grid_configure(padx=4, pady=4)
# focus
inValueEntry.focus()
# bind keys to convert (auto-update, no button)
root.bind('<KeyRelease>', conversion)
root.mainloop()
Could it be that with a readonly combobox the problem is not the selection but the relatively strong focus-indicator?
With this workarround you lose the ability to control your program by keyboard. To do it right you would have to change the style of the focus-highlighting.
from tkinter import *
from ttk import *
def defocus(event):
event.widget.master.focus_set()
root = Tk()
comboBox = Combobox(root, state="readonly", values=("a", "b", "c"))
comboBox.grid()
comboBox.set("a")
comboBox.bind("<FocusIn>", defocus)
mainloop()
You can use the Combobox's selection_clear() method to clear the selection whenever you want.
e.g
inUnitsValue.selection_clear()
Just refresh the selected value of the Combobox.
This will help for removing the highlight.
import tkinter.ttk
import tkinter
items = ["test1","test2","test3","test4"]
class TkCombobox(tkinter.ttk.Combobox):
def __init__(self, *arg, **kwarg):
super(TkCombobox, self).__init__(*arg, **kwarg)
self._strvar_ = tkinter.StringVar()
self._strvar_.set("")
self["textvariable"] = self._strvar_
self.bind("<<ComboboxSelected>>", self.highlight_clear)
def highlight_clear(self, event):
current = self._strvar_.get()
self.set("")
self.set(current)
master = tkinter.Tk();master.geometry("400x400")
c = TkCombobox(master, values=items, state="readonly")
c.pack()
master.mainloop()