A Tkinter Radiobutton is used when the user should select one option from a group of mutually exclusive choices. Radiobuttons in the same group share one Tkinter control variable, while each button has its own value.
When a Radiobutton is selected, its value is stored in the shared variable. The other buttons in that group are automatically deselected.
variable. Each button in the group should normally have a different value.
The following example creates three Radiobuttons. All three share the same StringVar, so only one choice can be selected at a time.
import tkinter as tk
root=tk.Tk()
root.geometry(
'350x150'
)
status=tk.StringVar(
value=''
)
r1=tk.Radiobutton(
root,
text='Passed',
variable=status,
value='Passed'
)
r2=tk.Radiobutton(
root,
text='Failed',
variable=status,
value='Failed'
)
r3=tk.Radiobutton(
root,
text='Appearing',
variable=status,
value='Appearing'
)
r1.grid(
row=0,
column=0,
padx=15,
pady=30
)
r2.grid(
row=0,
column=1
)
r3.grid(
row=0,
column=2
)
root.mainloop()
Because the initial value is an empty string and none of the Radiobuttons has value='', the group starts with no selection.
Two Radiobutton options are especially important:
| Option | Purpose |
|---|---|
variable | The shared Tkinter control variable used by the Radiobutton group. |
value | The value assigned to the shared variable when that Radiobutton is selected. |
For example:
choice=tk.StringVar()
r1=tk.Radiobutton(
root,
text='Python',
variable=choice,
value='Python'
)
r2=tk.Radiobutton(
root,
text='PHP',
variable=choice,
value='PHP'
)
If the user selects Python:
choice.get()
returns:
Python
If PHP is selected, the same variable returns:
PHP
A Radiobutton displays itself as selected when the shared variable equals its own value. Therefore, two Radiobuttons in one group should not normally use the same value.
Avoid this:
r1=tk.Radiobutton(
root,
text='Python',
variable=choice,
value=1
)
r2=tk.Radiobutton(
root,
text='PHP',
variable=choice,
value=1
)
Both buttons are testing the same shared variable against the same value, so the group no longer represents distinct choices correctly.
StringVar is convenient when the selected values are meaningful words.
import tkinter as tk
root=tk.Tk()
choice=tk.StringVar(
value='Python'
)
languages=[
'Python',
'PHP',
'JavaScript'
]
for column, language in enumerate(
languages
):
rb=tk.Radiobutton(
root,
text=language,
variable=choice,
value=language
)
rb.grid(
row=0,
column=column,
padx=10,
pady=20
)
root.mainloop()
Python is selected when the window opens because:
choice=tk.StringVar(
value='Python'
)
Use IntVar when each choice should store an integer.
status=tk.IntVar(
value=-1
)
r1=tk.Radiobutton(
root,
text='Passed',
variable=status,
value=1
)
r2=tk.Radiobutton(
root,
text='Failed',
variable=status,
value=0
)
r3=tk.Radiobutton(
root,
text='Appearing',
variable=status,
value=5
)
IntVar() normally starts with the value 0. If one Radiobutton has value=0, that button can appear selected immediately. Use an explicit unmatched value such as -1 when the group should initially have no selection.BooleanVar can be used for a two-choice group because Boolean values provide only True and False.
import tkinter as tk
root=tk.Tk()
result=tk.BooleanVar(
value=True
)
r1=tk.Radiobutton(
root,
text='Yes',
variable=result,
value=True
)
r2=tk.Radiobutton(
root,
text='No',
variable=result,
value=False
)
r1.pack()
r2.pack()
root.mainloop()
For groups with more than two choices, StringVar or IntVar is usually more suitable.
Set the shared variable to the value of the button that should initially be selected.
choice=tk.StringVar(
value='Python'
)
or after creating the variable:
choice.set(
'Python'
)
Any Radiobutton using:
value='Python'
will display as selected.
Set the variable to a value that does not match any Radiobutton in the group.
choice=tk.StringVar(
value=''
)
If the button values are Python, PHP and JavaScript, none will match the empty string.
choice=tk.IntVar(
value=-1
)
If the actual button values are 0, 1 and 2, the value -1 leaves the group unselected.
selected=choice.get()
print(
selected
)
choice.set(
'PHP'
)
The Radiobutton whose value='PHP' becomes selected automatically.
The command option runs a callback when the user invokes the Radiobutton.
import tkinter as tk
def show_selection():
print(
'Selected:',
choice.get()
)
root=tk.Tk()
choice=tk.StringVar(
value='Python'
)
r1=tk.Radiobutton(
root,
text='Python',
variable=choice,
value='Python',
command=show_selection
)
r2=tk.Radiobutton(
root,
text='PHP',
variable=choice,
value='PHP',
command=show_selection
)
r1.pack()
r2.pack()
root.mainloop()
The callback receives no event argument. Read the shared variable with get() to determine which choice was selected.
command responds to Radiobutton invocation. If you want to react whenever the associated variable changes, including changes made with set(), use trace_add().
import tkinter as tk
def selection_changed(*args):
print(
'New value:',
choice.get()
)
root=tk.Tk()
choice=tk.StringVar(
value=''
)
choice.trace_add(
'write',
selection_changed
)
for value in [
'Python',
'PHP',
'JavaScript'
]:
tk.Radiobutton(
root,
text=value,
variable=choice,
value=value
).pack(
anchor='w'
)
root.mainloop()
*args accepts the arguments supplied by the Tkinter variable trace callback.
A reliable way to clear a group is to set the shared variable to a value that none of its Radiobuttons uses.
choice.set(
''
)
choice.set(
-1
)
Choose a sentinel value that is not used by any actual choice.
Reset Form Data and SelectionsDifferent shared variables create different groups.
language=tk.StringVar()
level=tk.StringVar()
tk.Radiobutton(
root,
text='Python',
variable=language,
value='Python'
)
tk.Radiobutton(
root,
text='PHP',
variable=language,
value='PHP'
)
tk.Radiobutton(
root,
text='Beginner',
variable=level,
value='Beginner'
)
tk.Radiobutton(
root,
text='Advanced',
variable=level,
value='Advanced'
)
One language and one level can be selected because the two sets use different variables.

import tkinter as tk
def copy_selection():
group2.set(
group1.get()
)
root=tk.Tk()
root.geometry(
'400x180'
)
group1=tk.StringVar(
value='Passed'
)
group2=tk.StringVar(
value='Passed'
)
options=[
'Passed',
'Failed',
'Appearing'
]
for column, option in enumerate(
options
):
tk.Radiobutton(
root,
text=option,
variable=group1,
value=option,
command=copy_selection
).grid(
row=0,
column=column,
padx=10,
pady=20
)
tk.Radiobutton(
root,
text=option,
variable=group2,
value=option
).grid(
row=1,
column=column,
padx=10
)
root.mainloop()
When a button in the first group is selected, its value is copied into the variable used by the second group.
| Method | Action |
|---|---|
select() | Selects the Radiobutton and sets the shared variable to its value. |
deselect() | Deselects this Radiobutton if it is selected and clears the associated variable value. |
invoke() | Acts like the user selected the button: selects it and runs its command, unless disabled. |
flash() | Temporarily alternates the active and normal appearance without changing the final selection. |

import tkinter as tk
def show_value():
print(
'Value:',
choice.get()
)
root=tk.Tk()
root.geometry(
'500x220'
)
choice=tk.StringVar(
value='MySQL'
)
r1=tk.Radiobutton(
root,
text='PHP',
variable=choice,
value='PHP',
command=show_value
)
r2=tk.Radiobutton(
root,
text='MySQL',
variable=choice,
value='MySQL',
command=show_value
)
r3=tk.Radiobutton(
root,
text='Python',
variable=choice,
value='Python',
command=show_value
)
r1.grid(
row=0,
column=0,
padx=15,
pady=25
)
r2.grid(
row=0,
column=1
)
r3.grid(
row=0,
column=2
)
tk.Button(
root,
text='select()',
command=r1.select
).grid(
row=1,
column=0
)
tk.Button(
root,
text='deselect()',
command=r1.deselect
).grid(
row=1,
column=1
)
tk.Button(
root,
text='flash()',
command=r1.flash
).grid(
row=1,
column=2
)
tk.Button(
root,
text='invoke()',
command=r1.invoke
).grid(
row=1,
column=3
)
root.mainloop()
select() changes the selection. invoke() selects the Radiobutton and also calls its configured command.
Use state='disabled' to prevent user interaction. Restore the normal enabled state with state='normal'.
r1.config(
state='disabled'
)
r1.config(
state='normal'
)
import tkinter as tk
root=tk.Tk()
choice=tk.StringVar(
value='Passed'
)
r1=tk.Radiobutton(
root,
text='Passed',
variable=choice,
value='Passed'
)
r2=tk.Radiobutton(
root,
text='Failed',
variable=choice,
value='Failed',
state='disabled'
)
r1.pack()
r2.pack()
root.mainloop()
The classic Tk Radiobutton also understands an active state for its active visual appearance, but use normal when you want to enable a disabled button.
import tkinter as tk
root=tk.Tk()
root.geometry(
'220x150'
)
choice=tk.IntVar(
value=-1
)
bitmaps=[
('info', 1),
('question', 2),
('warning', 3)
]
for column, data in enumerate(
bitmaps
):
bitmap_name, value=data
tk.Radiobutton(
root,
bitmap=bitmap_name,
variable=choice,
value=value
).grid(
row=0,
column=column,
padx=15,
pady=40
)
root.mainloop()


The classic Tkinter Radiobutton normally displays a separate radio indicator. Setting indicatoron=0 removes the indicator and makes the complete widget appear more like a pushbutton.
import tkinter as tk
def selected():
print(
'Selected value:',
choice.get()
)
root=tk.Tk()
root.geometry(
'500x150'
)
choice=tk.StringVar(
value='Python'
)
options=[
'PHP',
'Python',
'JSP'
]
for column, option in enumerate(
options
):
rb=tk.Radiobutton(
root,
text=option,
variable=choice,
value=option,
command=selected,
indicatoron=0,
width=10,
padx=5,
pady=5
)
rb.grid(
row=0,
column=column,
padx=8,
pady=30
)
root.mainloop()
The buttons are still mutually exclusive because they continue to share the same variable.
Checkbutton Menu with indicatoronIf the number of choices comes from a list, database query or another data source, create the Radiobuttons in a loop.
import tkinter as tk
root=tk.Tk()
choice=tk.StringVar(
value=''
)
languages=[
'Python',
'PHP',
'JavaScript',
'SQL'
]
for row, language in enumerate(
languages
):
tk.Radiobutton(
root,
text=language,
variable=choice,
value=language
).grid(
row=row,
column=0,
sticky='w'
)
root.mainloop()
Only the data list has to change when more options are added.

A Radiobutton group can select a year that is later passed to a parameterized SQL query.
from datetime import datetime
import tkinter as tk
root=tk.Tk()
root.geometry(
'450x180'
)
current_year=datetime.now().year
years=[
current_year - 1,
current_year,
current_year + 1
]
selected_year=tk.IntVar(
value=current_year
)
query_label=tk.Label(
root,
text='Select a year',
bg='yellow',
width=50
)
query_label.grid(
row=1,
column=0,
columnspan=3,
padx=5,
pady=10
)
def update_query():
query="SELECT * FROM my_tasks WHERE strftime('%Y', dt) = ?"
params=(str(selected_year.get()),)
query_label.config(
text=f'{query} {params}'
)
for column, year in enumerate(
years
):
tk.Radiobutton(
root,
text=year,
variable=selected_year,
value=year,
command=update_query
).grid(
row=0,
column=column,
padx=10,
pady=20
)
root.mainloop()
The query and parameters can later be passed separately to the database API:
cursor.execute(
query,
params
)
This is preferable to manually concatenating user-controlled values into an SQL statement.
ttk.Radiobutton is the themed counterpart of the classic Tkinter Radiobutton. It uses the same important concepts: a shared variable and a unique value for each choice.
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
choice=tk.StringVar(
value='Python'
)
ttk.Radiobutton(
root,
text='Python',
variable=choice,
value='Python'
).pack(
anchor='w'
)
ttk.Radiobutton(
root,
text='PHP',
variable=choice,
value='PHP'
).pack(
anchor='w'
)
root.mainloop()
Classic options such as bg, fg, selectcolor and indicatoron are associated with the classic tk.Radiobutton. The themed ttk.Radiobutton is normally customized using ttk styles and states.
This creates independent buttons rather than one mutually exclusive group.
For one group, use one shared variable:
choice=tk.StringVar()
r1=tk.Radiobutton(
root,
variable=choice,
value='A'
)
r2=tk.Radiobutton(
root,
variable=choice,
value='B'
)
Each choice in one group should normally use a different value.
This can be unexpected:
choice=tk.IntVar()
r1=tk.Radiobutton(
root,
variable=choice,
value=0
)
Because an IntVar starts at 0, this Radiobutton can initially match the variable.
Use an unmatched default:
choice=tk.IntVar(
value=-1
)
A Boolean value only has two states. Use StringVar or IntVar for a larger group.
select() updates the selection. Use invoke() when you also want the Radiobutton's command callback to run.
Radiobuttons are designed for one selection from a group. Checkbuttons allow independent selections and therefore can support multiple checked options at the same time.
To clear the group, set the shared variable to a value that none of the buttons uses.
Keep SQL code and values separate and use database parameters where supported.
Example: selecting the PHP Radiobutton selects the PHP Checkbutton. Changing the choice to Python clears PHP and selects Python.
Radiobutton Exercise Solutionvalue.value in the shared variable.get() to read the current selection.set() to change the selection programmatically.StringVar is convenient for descriptive text values.IntVar is useful for numeric values.IntVar() normally starts at 0, so take care when one button also uses value=0.BooleanVar is practical for a two-choice True/False group.command callback runs when a Radiobutton is invoked.trace_add() can monitor changes to the shared variable, including programmatic changes.select() selects a button without calling its command callback.invoke() selects the button and runs its command unless the widget is disabled.deselect() clears the selected Radiobutton.flash() changes the appearance temporarily without changing the final selection.state='disabled' and state='normal' to disable or enable a Radiobutton.indicatoron=0 gives the classic Radiobutton a pushbutton-style appearance.ttk.Radiobutton provides the themed Radiobutton widget.Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.
14-03-2024 | |
| Hi, my radio buttons using customtkinter work just debbuging into VSCODE, but when I use pyinstaller for .exe they not show, i print the error in messagebox and the error is just 'CTkRadiobutton' | |