tkinter radiobutton event when variable change - python-3.x

I am a beginner in tkinter, and I reach a trouble.
I would like to have a radio button group which enable or disable some buttons, scales, ..., in function of which radio button is selected. Those button group are connected to a variable. To disable/enable widgets, I use button group command. Everything works when I click on radio buttons. But if the variable changed, the radio button changed without calling the radio command, so other widgets are not updated.
Here is a very simple code of what I want to do
from tkinter import ttk
import tkinter as tk
root = tk.Tk()
frame = ttk.LabelFrame(root, text='choice your futur')
frame.pack(fill="both", expand="yes", padx=5, pady=5)
selection = tk.IntVar()
def onButtonClic():
selection.set(1)
bt = tk.Button(frame, text='continue', command=onButtonClic)
bt.grid(column=0, row=1, columnspan=2, sticky='ew')
def onRadioButtonChange():
if selection.get() != 0:
bt.configure(state = tk.DISABLED)
else:
bt.configure(state = tk.NORMAL)
tk.Radiobutton(frame, command=onRadioButtonChange, text = "blue pill", variable = selection, value = 0).grid(column=0, row=0, sticky='nw')
tk.Radiobutton(frame, command=onRadioButtonChange, text = "red pill", variable = selection, value = 1).grid(column=1, row=0, sticky='nw')
root.mainloop()
If I select the red pill, button is disabled. When blue pill is selected, and click on button (which set radio variable to 1: the red pill value), the red pill is selected, but button is still enable.
I would like when the variable changed, then the radio command be called.
Best regards
JM

You can use tkinter variable .trace_add() function instead:
...
def onRadioButtonChange(*args):
if selection.get() != 0:
bt.configure(state = tk.DISABLED)
else:
bt.configure(state = tk.NORMAL)
# call onRadioButtonChange() when the variable is updated
selection.trace_add('write', onRadioButtonChange)
tk.Radiobutton(frame, text = "blue pill", variable = selection, value = 0).grid(column=0, row=0, sticky='nw')
tk.Radiobutton(frame, text = "red pill", variable = selection, value = 1).grid(column=1, row=0, sticky='nw')
...

Related

Tkinter dialog's elements position

I am building custom Tkinter dialog window with Entry and Combobox. I am stuck with placing text and enter frames. Currently I am placing them manually. I am looking for the way to let tkinter do it automatically (maybe with pack() method). And also configure TopLevel size automatically.
My code:
def ask_unit_len():
values = ['millimeters', 'micrometers', 'nanometers']
top = Toplevel()
top.geometry('170x100')
top.resizable(False, False)
top.focus_set()
top.grab_set()
top.title('Enter length and units')
label_length = Label(top, text='Length:')
label_length.place(x=0, y=0)
units_type = StringVar()
length = StringVar()
answer_entry = Entry(top, textvariable=length, width=10)
answer_entry.place(x=55, y=0)
label_units = Label(top, text='Units:')
label_units.place(x=0, y=30)
combo = Combobox(top, width=10, textvariable=units_type,
values=values)
combo.place(x=50, y=30)
button = Button(top, text='Enter',
command=lambda:
mb.showwarning("Warning",
"Enter all parameters correctly")
if (units_type.get() == "" or not length.get().isdigit()
or int(length.get()) <= 0)
else top.destroy())
button.place(x=65, y=70)
top.wait_window(top)
return int(length.get()), units_type.get()
So, is there any way to perform this?

Is there a way to make sure a highlited parent widget remains highlighted when selecting a child widget

I have a radiobutton that highlights the corresponding LabelFrame.
Each LabelFrame has an Entry widget as a child.
When the Entry widget is selected to type in some input, the parent LabelFrame loses the given highlightbackground color (from cyan to gray) but keeps the same highlightthickness.
Is there a way to keep the given highlightbackground color?
(windows 7 64, pycharm 2019.2)
Thanks in advance.
from tkinter import *
from tkinter import ttk
import tkinter as tk
class doSomeStuff(Tk):
def __init__(self):
Tk.__init__(self)
self.radioBtnVar = StringVar() # radiobutton variable
# main canvas
pwdCanvas = tk.Canvas(self, bd=0, highlightthickness=0)
pwdCanvas.pack()
# choiceLabelFrame
choiceLabelFrame = ttk.LabelFrame(pwdCanvas, text='Choice LabelFrame (ttk)')
choiceLabelFrame.grid(column=0, row=11, columnspan=2, sticky='nsew')
# radio button 1
rbtn1 = ttk.Radiobutton(choiceLabelFrame, text='A', variable=self.radioBtnVar, value='PCG', command=self.colorLabels)
rbtn1.pack(side='left')
# radio button 2
rbtn2 = ttk.Radiobutton(choiceLabelFrame, text='B', variable=self.radioBtnVar, value='UG', command=self.colorLabels)
rbtn2.pack(side='right')
# LabelFrame1, left side
self.LabelFrame1 = tk.LabelFrame(pwdCanvas, text="LabelFrame 1 (tk)", bd=0) # I use tk to have access to the 'highlightbackground' option
self.LabelFrame1.grid(column=0, row=12, sticky='nsew', padx=3, pady=3)
entry1Label = ttk.Label(self.LabelFrame1, text='Entry 1')
entry1Label.grid(column=0, row=11, sticky='w')
self.labelEntry1 = ttk.Entry(self.LabelFrame1, state='disabled')
self.labelEntry1.grid(column=1, row=11, sticky='w')
# LabelFrame2, right side
self.LabelFrame2 = tk.LabelFrame(pwdCanvas, text="LabelFrame 2 (tk)", bd=0)
self.LabelFrame2.grid(column=1, row=12, sticky='nw', padx=3, pady=3)
entry2Label = ttk.Label(self.LabelFrame2, text='Entry 2')
entry2Label.grid(column=0, row=0)
labelEntry2 = ttk.Entry(self.LabelFrame2, state='disabled')
labelEntry2.grid(column=1, row=0)
def colorLabels(self): # activates and highlights the chosen option
if self.radioBtnVar.get() == 'PCG':
for child in self.LabelFrame1.winfo_children():
child.config(state='enabled')
self.LabelFrame1.config(highlightbackground='cyan', highlightthickness=2)
for child in self.LabelFrame2.winfo_children():
child.config(state='disabled')
self.LabelFrame2.config(highlightthickness=0)
elif self.radioBtnVar.get() == 'UG':
for child in self.LabelFrame2.winfo_children():
child.config(state='enabled')
self.LabelFrame2.config(highlightbackground='cyan', highlightthickness=2)
for child in self.LabelFrame1.winfo_children():
child.config(state='disabled')
self.LabelFrame1.config(highlightthickness=0)
if __name__ == "__main__":
app = doSomeStuff()
app.mainloop()
The highlightthickness attribute is specifically for highlighting which widget has the keyboard focus. It serves as a clue for the user when traversing the UI with the keyboard.
Because it is tied directly to which widget has focus, and because you can only have focus in one widget at a time, it's not possible to use that feature to highlight more than one thing at a time.
I've found a way to get what I want.
def colorLabels(self):
if self.radioBtnVar.get() == 'PCG':
for child in self.LabelFrame1.winfo_children():
child.config(state='enabled')
self.LabelFrame1.config(highlightbackground='cyan', highlightcolor='cyan', highlightthickness=2)
for child in self.LabelFrame2.winfo_children():
child.config(state='disabled')
self.LabelFrame2.config(highlightthickness=0)
elif self.radioBtnVar.get() == 'UG':
for child in self.LabelFrame2.winfo_children():
child.config(state='enabled')
self.LabelFrame2.config(highlightbackground='cyan', highlightcolor='cyan', highlightthickness=2)
for child in self.LabelFrame1.winfo_children():
child.config(state='disabled')
self.LabelFrame1.config(highlightthickness=0)
I simply added 'highlightcolor='cyan''.
As explained here effbot.org :
'highlightbackground' is used when the widget doesn’t have focus.
'highlightcolor' is used when the widget has focus.
That way my widget keeps its highlighted contour even if it's not in focus.

Dynamic Form Widget interaction in python tkinter

I am trying to generate a blank GUI, with 1 menu Item.
I then use a function to generate a label, a button and an entry widget on the same form when the selection is made from the menu item.
However when I try to use the get() method to get the value of the input in the generated textbox, I get an error. I may have missed some core concept here and this may not be possible, but I would like to know. Following is my code,
from tkinter import Tk, Label, Button, Entry, Menu
def btn_clientadd():
print(txt1.get())
def addclient():
lbl1 = Label(window, text="Client Name :")
lbl1.grid(row=1,column=1,padx=7,pady=7,sticky='e')
txt1 = Entry(window)
txt1.grid(row=1, column=2)
txt1.focus()
btn = Button(window, text="Add Client", command=btn_clientadd)
btn.grid(row=2,column=2,padx=7,pady=7)
window = Tk()
window.geometry('400x200')
menu = Menu(window)
new_item1 = Menu(menu)
menu.add_cascade(label='ClientMaster', menu=new_item1)
new_item1.add_command(label='Add New Client', command=addclient)
window.config(menu=menu)
window.mainloop()
The entry txt1 is created inside a function and the reference to it is garbage collected when the function ends. One way you can get around this it to declare a StringVar() in the global scope and then associate it to the entry.
Examine the example below:
from tkinter import Tk, Label, Button, Entry, Menu, StringVar
def btn_clientadd():
print(client_string.get()) # Get contents of StringVar
def addclient():
lbl1 = Label(window, text="Client Name :")
lbl1.grid(row=1,column=1,padx=7,pady=7,sticky='e')
# Create entry and associate it with a textvariable
txt1 = Entry(window, textvariable=client_string)
txt1.grid(row=1, column=2)
txt1.focus()
btn = Button(window, text="Add Client", command=btn_clientadd)
btn.grid(row=2,column=2,padx=7,pady=7)
window = Tk()
window.geometry('400x200')
menu = Menu(window)
new_item1 = Menu(menu)
menu.add_cascade(label='ClientMaster', menu=new_item1)
new_item1.add_command(label='Add New Client', command=addclient)
window.config(menu=menu)
client_string = StringVar() # StringVar to associate with entry
window.mainloop()

Python 3 Radio button controlling label text

I am in the process of learning Python3 and more of a necessity, the TkInter GUI side. I was working my way through a book by James Kelly, when I encountered this problem. All his examples made a new window with just label/canvas/check box etc which seemed to work OK.
But as I wanted to experiment in a more real world scenario I put most things on one window. This where I encountered my problem. I can not get the radio button in the frame to alter the wording of a label in the parent window.
Complete code is:-
#! /usr/bin/python3
from tkinter import *
def win_pos(WL,WH,xo=0,yo=0) :
# Screen size & position procedure
# Screen size
SW = home.winfo_screenwidth()
SH = home.winfo_screenheight()
# 1/2 screen size
sw=SW/2
sh=SH/2
# 1/2 window size
wl=WL/2
wh=WH/2
# Window position
WPx=sw-wl+xo
WPy=sh-wh+yo
# Resulting string
screen_geometry=str(WL) + "x" + str(WH) + "+" + str(int(WPx)) + "+" \ + str(int(WPy))
return screen_geometry
# Create a window
home=Tk()
home.title("Radio buttons test")
# Set the main window
home.geometry(win_pos(600,150))
lab1=Label(home)
lab1.grid(row=1,column=1)
fraym1=LabelFrame(home, bd=5, bg="red",relief=SUNKEN, text="Label frame text")
fraym1.grid(row=2,column=2)
laybl1=Label(fraym1, text="This is laybl1")
laybl1.grid(row=0, column=3)
var1=IntVar()
R1=Radiobutton(fraym1, text="Apple", variable=var1, value=1)
R1.grid(row=1, column=1)
R2=Radiobutton(fraym1, text="Asus", variable=var1, value=2)
R2.grid(row=1, column=2)
R3=Radiobutton(fraym1, text="HP", variable=var1, value=3)
R3.grid(row=1, column=3)
R4=Radiobutton(fraym1, text="Lenovo", variable=var1, value=4)
R4.grid(row=1, column=4)
R5=Radiobutton(fraym1, text="Toshiba", variable=var1, value=5)
R5.grid(row=1, column=5)
# Create function used later
def sel(var) :
selection="Manufacturer: "
if var.get() > 0 :
selection=selection + str(var.get())
lab1.config(text=selection)
R1.config(command=sel(var1))
R2.config(command=sel(var1))
R3.config(command=sel(var1))
R4.config(command=sel(var1))
R5.config(command=sel(var1))
R1.select()
mainloop()
I realise that there is room for improvement using classes/functions but I need to get this resolved in my head before I move on. As it can be hopefully seen, I'm not a complete novice to programming, but this is doing my head in.
Can a solution, and reasoning behind the solution, be given?
You can modify your label's text by assigning the same variable class object, var1 as its textvariable option as well but since lab1's text is slightly different, try removing:
R1.config(command=sel(var1))
R2.config(command=sel(var1))
R3.config(command=sel(var1))
R4.config(command=sel(var1))
R5.config(command=sel(var1))
R1.select()
and modify sel to:
def sel(*args) :
selection="Manufacturer: "
selection=selection + str(var1.get())
lab1.config(text=selection)
and then call var1.trace("w", sel) somewhere before mainloop as in:
...
var1.trace("w", sel)
mainloop()
Also for a simple example:
import tkinter as tk
root = tk.Tk()
manufacturers = ["man1", "man2", "man3", "man4", "man5"]
lbl = tk.Label(root, text="Please select a manufacturer.")
lbl.pack()
# create an empty dictionary to fill with Radiobutton widgets
man_select = dict()
# create a variable class to be manipulated by radiobuttons
man_var = tk.StringVar(value="type_default_value_here_if_wanted")
# fill radiobutton dictionary with keys from manufacturers list with Radiobutton
# values assigned to corresponding manufacturer name
for man in manufacturers:
man_select[man] = tk.Radiobutton(root, text=man, variable=man_var, value=man)
#display
man_select[man].pack()
def lbl_update(*args):
selection="Manufacturer: "
selection=selection + man_var.get()
lbl['text'] = selection
#run lbl_update function every time man_var's value changes
man_var.trace('w', lbl_update)
root.mainloop()
Example with label's identical to that of radiobutton's value:
import tkinter as tk
root = tk.Tk()
# radiobutton group will the button selected with the value=1
num = tk.IntVar(value=1)
lbl = tk.Label(root, textvariable=num)
zero = tk.Radiobutton(root, text="Zero", variable=num, value=0)
one = tk.Radiobutton(root, text="One", variable=num, value=1)
#display
lbl.pack()
zero.pack()
one.pack()
root.mainloop()

Python 3.3 TkInter window closes unexpectedly. Why?

I came up with the following code more as a reference to help me remember how to build GUI apps with TkInter. It runs great except when a click Button1 or any other widget whose command option is set to self.hello. As you can see in the code bellow, the hello function is like a place holder. While the button click works fine while running the script through IDLE, it simply causes the application to exit if you start the program by double-clicking the actual file test.pyw. My question is, why?
#Some guy somewhere
from tkinter import *
class Application:
def hello(self):
msg = messagebox.showinfo('Message Title','Message Body')
def __init__(self, parent):
parent.resizable(0,0)
parent.minsize(800, 400)
parent.title('Top Level')
# Global Padding pady and padx
pad_x = 0
pad_y = 0
# CASCADE MENU
# create a parent menu.
self.parentmenu1 = Menu(parent, tearoff=0)
#self.menubar1.add_command(label='Menu1', command=self.hello)
#create a child menu for parent menu.
self.parentmenu1_child1 = Menu(parent, tearoff=0)
self.parentmenu1_child1.add_command(label='Item1', command=self.hello)
self.parentmenu1_child1.add_command(label='Item2', command=self.hello)
self.parentmenu1_child1.add_command(label='Item3', command=self.hello)
#add child menu to parent menu.
self.parentmenu1.add_cascade(label='Menu1', menu=self.parentmenu1_child1)
#self.menubar1.add_separator()
# SINGLE MENU
# create a parent menu.
self.parentmenu1.add_command(label='Menu2', command=self.hello)
# SINGLE MENU
# create a parent menu.
self.parentmenu1.add_command(label='Menu3', command=self.hello)
# display the parent menu.
parent.config(menu=self.parentmenu1)
# Create controls
#create label
self.label1 = Label(parent, text='Label1')
#create textbox
self.textbox1 = Entry(parent)
#create button
self.button1 = Button(parent, text='Button1', command=self.hello)
#string variable to hold checkbox1 values.
self.str_checkbox1 = StringVar()
#create checkbox
self.checkbox1 = Checkbutton(parent, text='Checkbox1', variable=self.str_checkbox1, onvalue='on1', offvalue='off1')
#deselect checkbox1
self.checkbox1.deselect()
#string variable to hold checkbox2 values.
self.str_checkbox2 = StringVar()
#create checkbox
self.checkbox2 = Checkbutton(parent, text='Checkbox2', variable=self.str_checkbox2, onvalue='on2', offvalue='off2')
#deselect checkbox2
self.checkbox2.deselect()
#???? ..what sets the groupbox apart from others. primary key???!!
self.str_radiobutton1 = StringVar()
#command= parameter missing.
self.radiobutton1 = Radiobutton(parent, text='Radio 1', variable=self.str_radiobutton1, value='a')
self.radiobutton2 = Radiobutton(parent, text='Radio 2', variable=self.str_radiobutton1, value='b')
self.radiobutton1.select()
#create a list of options.
optionList = ('Option1', 'Option2', 'Option3')
#string variable to hold optionlist values.
self.str_optionmenu1 = StringVar()
#associate string variable with optionlist
self.str_optionmenu1.set(optionList[0])
#create optionmenu
self.optionmenu1 = OptionMenu(parent, self.str_optionmenu1, *optionList)
#create a frame
self.frame1 = Frame(parent)
#create a text.
self.textarea1 = Text(self.frame1, width=20, height=10)
#align text left and fill frame with it.
self.textarea1.pack(side=LEFT, fill=Y)
#create a scrollbar.
self.scrollbar1 = Scrollbar(self.frame1)
#align scrollbar right and fill frame with it.
self.scrollbar1.pack(side=RIGHT, fill=Y)
#what is going to be scrolled?
self.scrollbar1.config(command=self.textarea1.yview)
#set textarea scrollbar.
self.textarea1.config(yscrollcommand=self.scrollbar1.set)
#align frame left and fill.
self.frame1.pack(side=LEFT, fill=Y)
#create a frame
self.frame2 = Frame(parent)
#create a text.
self.listbox1 = Listbox(self.frame2, width=20, height=10, activestyle='none', selectmode=SINGLE)
#create a list of items.
optionList = ('Item1', 'Item2', 'Item3', 'Item4', 'Item5', 'Item6', 'Item7', 'Item8', 'Item9', 'Item10', 'Item11')
#add items from list to listbox
for item in optionList:
self.listbox1.insert(END, item)
#align text left and fill frame with it.
self.listbox1.pack(side=LEFT, fill=Y)
#create a scrollbar.
self.scrollbar2 = Scrollbar(self.frame2)
#align scrollbar right and fill frame with it.
self.scrollbar2.pack(side=RIGHT, fill=Y)
#what is going to be scrolled?
self.scrollbar2.config(command=self.listbox1.yview)
#set textarea scrollbar.
self.listbox1.config(yscrollcommand=self.scrollbar2.set)
#align frame left and fill.
self.frame2.pack(side=LEFT, fill=Y)
# Place controls inside of grid
self.label1.grid(row=0, column=0, padx=pad_x, pady=pad_y, sticky=W)
self.textbox1.grid(row=0, column=1, padx=pad_x, pady=pad_y, sticky=W)
self.button1.grid(row=1, column=0, padx=pad_x, pady=pad_y, sticky=W)
self.checkbox1.grid(row=1, column=1, padx=pad_x, pady=pad_y, sticky=W)
self.checkbox2.grid(row=1, column=2, padx=pad_x, pady=pad_y, sticky=W)
self.optionmenu1.grid(row=2, column=0, padx=pad_x, pady=pad_y, sticky=W)
self.frame1.grid(row=2, column=1, padx=pad_x, pady=pad_y, sticky=W)
self.radiobutton1.grid(row=3, column=0, padx=pad_x, pady=pad_y, sticky=W)
self.radiobutton2.grid(row=3, column=1, padx=pad_x, pady=pad_y, sticky=W)
self.frame2.grid(row=4, column=0, padx=pad_x, pady=pad_y, sticky=W)
if __name__ == '__main__':
parent = Tk()
app = Application(parent)
parent.mainloop()
Alright. Apparently tkMessageBox has been renamed to messagebox in Python 3.x. Also this
module is not available in tkinter so even though a developer might use:
from tkinter import *
..he/she would still need to:
from tkinter import messagebox

Resources