How can I move the text label of a radiobutton below the button in Python Tkinter?
Asked Answered
C

3

8

I'm wondering if there's a way to move the label text of a radiobutton to a different area, e.g. below the actual button.

Below is an example of a few radiobuttons being placed using grid that I'm using:

from tkinter import *
from tkinter import ttk


class MainGUI(Frame):

    def __init__(self, parent):
        self.parent = parent
        Frame.__init__(self, parent, background="white")
        self.mainframe = ttk.Frame(root, padding="8 8 12 12")
        self.mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
        self.mainframe.columnconfigure(0, weight=1)
        self.mainframe.rowconfigure(0, weight=1)
        ttk.Radiobutton(self.mainframe, text="Button 1", value=0).grid(column=1, row=2)
        ttk.Radiobutton(self.mainframe, text="Button 2", value=1).grid(column=2, row=2)
        ttk.Radiobutton(self.mainframe, text="Button 3", value=2).grid(column=3, row=2)
        ttk.Radiobutton(self.mainframe, text="Button 4", value=3).grid(column=4, row=2)

if __name__ == '__main__':
    root = Tk()
    root.geometry("300x100")
    app = MainGUI(root)
    root.mainloop()

Below is an image of the frame when the code is run:

As you can see the default is to be on the side of the buttons, but I'm looking for a way to move the text below, I can't seem to find any solutions to this question!

Thanks, Raj

Cardenas answered 9/1, 2017 at 15:26 Comment(1)
Well asked question! Welcome to StackOverflow :) You might want to take the tour as well :)Lutherlutheran
H
4

My thought is to try amending the ttk.RadioButton's layout elements or element's option. See http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/ttk-layouts.html.

The layout of its element seems to consist of a label, that is inside the focus, that is inside indicator, that is inside the padding (See below variable rbLayout). The Radiobutton.indicator's side option value is left. The Radiobutton.label's anchor option appears blank. I think changing either of these options may get what you want. You would also have to add the option "style=your_customed_stylename" into your ttk.Radiobutton declaration.

>>> import tkinter.ttk as ttk
>>> s = ttk.Style()
>>> rb = ttk.Radiobutton(None, text='RB1')
>>> rbClass = rb.winfo_class()
>>> rbClass
'TRadiobutton'
>>> rbLayout = s.layout('TRadiobutton')
>>> rbLayout
[('Radiobutton.padding', {'sticky': 'nswe', 'children': [('Radiobutton.indicator', {'sticky': '', 'side': 'left'}), ('Radiobutton.focus', {'children': [('Radiobutton.label', {'sticky': 'nswe'})], 'sticky': '', 'side': 'left'})]})]
>>> type(rbLayout)
<class 'list'>

Update:

  1. I think the rbLayout variable shows that by default the Radiobutton.padding contains two children elements, i.e. Radiobutton.indicator and Radiobutton.focus, and they are positioned on the left side of Radiobutton.padding. Furthermore, Radiobutton.focus contains Radiobutton.label. Correction to my earlier explanation.
  2. To achieve the described layout, I think Radiobutton.indicator's 'side' key should have value 'top' and 'sticky' key should have value 'n'. Furthermore, Radiobutton.focus's 'side' key should have value 'bottom' and 'sticky' value should be 's'.
  3. I created a script to implemented the changes. See below code. However, it did not work. I am surprise the radiobutton layout did not change. Have not understood why it did not work.

Hope someone more knowledgeable can explain why the amendments to Radiobutton element's layout did not change to the required layout.

import tkinter as tk
import tkinter.ttk as ttk

root = tk.Tk()
root.geometry("300x100")

s = ttk.Style()
m = s.layout('TRadiobutton')
print('TRadiobutton.Layout : Default')
print(m , "\n")

# Change to default Radiobutton.indicator elements
list(list(m[0])[1]['children'][0])[1]['side'] = 'top'
list(list(m[0])[1]['children'][0])[1]['sticky'] = 'n'

# Change to default Radiobutton.focus elements
list(list(m[0])[1]['children'][1])[1]['side']='bottom'
list(list(m[0])[1]['children'][1])[1]['sticky']='s'

print('TRadiobutton.Layout : Amended')
print(m, "\n")

frame1 = ttk.Frame(root)
frame1.grid()
rb1 = ttk.Radiobutton(frame1, text="Button 1")
rb1.grid()

root.rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1)

Update 2 : SOLUTION

import tkinter as tk
import tkinter.ttk as ttk

root = tk.Tk()
root.geometry("300x100")

s = ttk.Style()
s.theme_use('default')

print('TRadiobutton.Layout : Default')
print(s.layout('TRadiobutton'), "\n")

s.layout('TRadiobutton',
         [('Radiobutton.padding',
           {'children':
            [('Radiobutton.indicator', {'side': 'top', 'sticky': ''}), # Just need to change indicator's 'side' value
             ('Radiobutton.focus', {'side': 'left',
                                    'children':
                                    [('Radiobutton.label', {'sticky': 'nswe'})],
                                    'sticky': ''})],
            'sticky': 'nswe'})])

print('TRadiobutton.Layout : Amended')
print(s.layout('TRadiobutton'), "\n")

frame1 = ttk.Frame(root)
frame1.grid()
rb1 = ttk.Radiobutton(frame1, text="Button 1")
rb1.grid()

root.rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1)

Radiobutton with top indicator and bottom label

A big thanks :) to j_4321 for his latest solution building on my suggested approach. I have implemented it (see above) to my previous code and it works. I like to highlight the following:

  1. My code shows changes made to the layout of the Radiobutton's default style 'TRadiobutton' instead of creating a new Style with a changed layout. The advantage of this difference appears to overcome the shortcoming mention by j_4321 2nd solution. That is, it works on all ttk themes ('clam', 'alt', 'default', 'classic') and not just on 'clam' and 'alt'. At least this works on my Linux 16.04 platform.
  2. The mistake in my earlier code was that I had created a new instance of s.layout() (i.e. m = s.layout()) and changed it's layout, instead of actually changing the layout of the default Radiobutton style s.layout('TRadiobutton'). I learned from j_4321's solution that one can change the layout of a default style by adding the changed details of a widget's layout as the 2nd term in the tuple ttk.Style.layout(widget's default style name), i.e. ttk.Style.layout(widget's default style name e.g. 'TRadiobutton', [changed layout details of widget]).
  3. To change the Radiobutton indicator's location to appear at the top of it's label, change to the Radiobutton.indicator's 'side' value to 'top' is only needed. Changes to the Radiobutton.focus's 'side' value had no effect on Radiobutton's layout.
Haversine answered 10/1, 2017 at 5:21 Comment(2)
You need to pass the amended layout in argument to s.layout: see my second answer for a working example.Embryology
@Embryology Great thanks! I learned from your solution and updated my code (see Update 2 : SOLUTION). Also, pls note my new finding which overcome the limitations you had mentioned (works for all ttk.Style themes) and observation (change to indicator's 'side' value is only needed ) .Haversine
E
4

There is no option to set the text position in a radiobutton, so the easiest workaround I can think of is to remove the radiobutton text and put a label beneath it.

In addition, I would rather make a MainGUI class inherit from Tk instead of Frame so that you don't have to create a root window in the if __name__ == '__main__': section, but it's just a suggestion.

from tkinter import *
from tkinter import ttk

class MainGUI(Tk):

    def __init__(self):
        Tk.__init__(self)
        self.geometry("300x100")
        self.configure(background="white")
        self.mainframe = ttk.Frame(self, padding="8 8 12 12")
        self.mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
        self.mainframe.columnconfigure(0, weight=1)
        self.mainframe.rowconfigure(0, weight=1)
        ttk.Radiobutton(self.mainframe,  value=0).grid(column=1, row=2)
        ttk.Label(self.mainframe, text="Button 1").grid(column=1, row=3, padx=4)
        ttk.Radiobutton(self.mainframe,  value=1).grid(column=2, row=2)
        ttk.Label(self.mainframe, text="Button 2").grid(column=2, row=3, padx=4)
        ttk.Radiobutton(self.mainframe,  value=2).grid(column=3, row=2)
        ttk.Label(self.mainframe, text="Button 3").grid(column=3, row=3, padx=4)
        ttk.Radiobutton(self.mainframe,  value=3).grid(column=4, row=2)
        ttk.Label(self.mainframe, text="Button 4").grid(column=4, row=3, padx=4)
        self.mainloop()

if __name__ == '__main__':
    app = MainGUI()

Result

Remark: to get this picture, I also used a ttk.Style to get a nicer result in linux.

Embryology answered 9/1, 2017 at 15:48 Comment(0)
H
4

My thought is to try amending the ttk.RadioButton's layout elements or element's option. See http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/ttk-layouts.html.

The layout of its element seems to consist of a label, that is inside the focus, that is inside indicator, that is inside the padding (See below variable rbLayout). The Radiobutton.indicator's side option value is left. The Radiobutton.label's anchor option appears blank. I think changing either of these options may get what you want. You would also have to add the option "style=your_customed_stylename" into your ttk.Radiobutton declaration.

>>> import tkinter.ttk as ttk
>>> s = ttk.Style()
>>> rb = ttk.Radiobutton(None, text='RB1')
>>> rbClass = rb.winfo_class()
>>> rbClass
'TRadiobutton'
>>> rbLayout = s.layout('TRadiobutton')
>>> rbLayout
[('Radiobutton.padding', {'sticky': 'nswe', 'children': [('Radiobutton.indicator', {'sticky': '', 'side': 'left'}), ('Radiobutton.focus', {'children': [('Radiobutton.label', {'sticky': 'nswe'})], 'sticky': '', 'side': 'left'})]})]
>>> type(rbLayout)
<class 'list'>

Update:

  1. I think the rbLayout variable shows that by default the Radiobutton.padding contains two children elements, i.e. Radiobutton.indicator and Radiobutton.focus, and they are positioned on the left side of Radiobutton.padding. Furthermore, Radiobutton.focus contains Radiobutton.label. Correction to my earlier explanation.
  2. To achieve the described layout, I think Radiobutton.indicator's 'side' key should have value 'top' and 'sticky' key should have value 'n'. Furthermore, Radiobutton.focus's 'side' key should have value 'bottom' and 'sticky' value should be 's'.
  3. I created a script to implemented the changes. See below code. However, it did not work. I am surprise the radiobutton layout did not change. Have not understood why it did not work.

Hope someone more knowledgeable can explain why the amendments to Radiobutton element's layout did not change to the required layout.

import tkinter as tk
import tkinter.ttk as ttk

root = tk.Tk()
root.geometry("300x100")

s = ttk.Style()
m = s.layout('TRadiobutton')
print('TRadiobutton.Layout : Default')
print(m , "\n")

# Change to default Radiobutton.indicator elements
list(list(m[0])[1]['children'][0])[1]['side'] = 'top'
list(list(m[0])[1]['children'][0])[1]['sticky'] = 'n'

# Change to default Radiobutton.focus elements
list(list(m[0])[1]['children'][1])[1]['side']='bottom'
list(list(m[0])[1]['children'][1])[1]['sticky']='s'

print('TRadiobutton.Layout : Amended')
print(m, "\n")

frame1 = ttk.Frame(root)
frame1.grid()
rb1 = ttk.Radiobutton(frame1, text="Button 1")
rb1.grid()

root.rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1)

Update 2 : SOLUTION

import tkinter as tk
import tkinter.ttk as ttk

root = tk.Tk()
root.geometry("300x100")

s = ttk.Style()
s.theme_use('default')

print('TRadiobutton.Layout : Default')
print(s.layout('TRadiobutton'), "\n")

s.layout('TRadiobutton',
         [('Radiobutton.padding',
           {'children':
            [('Radiobutton.indicator', {'side': 'top', 'sticky': ''}), # Just need to change indicator's 'side' value
             ('Radiobutton.focus', {'side': 'left',
                                    'children':
                                    [('Radiobutton.label', {'sticky': 'nswe'})],
                                    'sticky': ''})],
            'sticky': 'nswe'})])

print('TRadiobutton.Layout : Amended')
print(s.layout('TRadiobutton'), "\n")

frame1 = ttk.Frame(root)
frame1.grid()
rb1 = ttk.Radiobutton(frame1, text="Button 1")
rb1.grid()

root.rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1)

Radiobutton with top indicator and bottom label

A big thanks :) to j_4321 for his latest solution building on my suggested approach. I have implemented it (see above) to my previous code and it works. I like to highlight the following:

  1. My code shows changes made to the layout of the Radiobutton's default style 'TRadiobutton' instead of creating a new Style with a changed layout. The advantage of this difference appears to overcome the shortcoming mention by j_4321 2nd solution. That is, it works on all ttk themes ('clam', 'alt', 'default', 'classic') and not just on 'clam' and 'alt'. At least this works on my Linux 16.04 platform.
  2. The mistake in my earlier code was that I had created a new instance of s.layout() (i.e. m = s.layout()) and changed it's layout, instead of actually changing the layout of the default Radiobutton style s.layout('TRadiobutton'). I learned from j_4321's solution that one can change the layout of a default style by adding the changed details of a widget's layout as the 2nd term in the tuple ttk.Style.layout(widget's default style name), i.e. ttk.Style.layout(widget's default style name e.g. 'TRadiobutton', [changed layout details of widget]).
  3. To change the Radiobutton indicator's location to appear at the top of it's label, change to the Radiobutton.indicator's 'side' value to 'top' is only needed. Changes to the Radiobutton.focus's 'side' value had no effect on Radiobutton's layout.
Haversine answered 10/1, 2017 at 5:21 Comment(2)
You need to pass the amended layout in argument to s.layout: see my second answer for a working example.Embryology
@Embryology Great thanks! I learned from your solution and updated my code (see Update 2 : SOLUTION). Also, pls note my new finding which overcome the limitations you had mentioned (works for all ttk.Style themes) and observation (change to indicator's 'side' value is only needed ) .Haversine
E
3

As Sun Bear suggested, another method to get the radiobutton label beneath the button is to customize the ttk style:

from tkinter import *
from tkinter import ttk

class MainGUI(Tk):

    def __init__(self):
        Tk.__init__(self)
        self.geometry("300x100")
        self.configure(background="white")
        self.mainframe = ttk.Frame(self, padding="8 8 12 12")
        self.mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
        self.mainframe.columnconfigure(0, weight=1)
        self.mainframe.rowconfigure(0, weight=1)
        style = ttk.Style(self)
        style.theme_use('clam')
        # create custom radiobutton style layout
        style.layout('CustomRadiobutton', 
                     [('Radiobutton.padding',
                       {'children': 
                           [('Radiobutton.indicator', 
                             {'side': 'top', 'sticky': ''}), # changed side from left to top
                            ('Radiobutton.focus',
                             {'children': [('Radiobutton.label', {'sticky': 'nswe'})],
                              'side': 'top', # changed side from left to top
                              'sticky': ''})],
                        'sticky': 'nswe'})])
        style.configure('TFrame', background="white")
        style.configure('CustomRadiobutton', background="white")
        ttk.Radiobutton(self.mainframe, style='CustomRadiobutton',
        text="Button 1", value=0).grid(column=1, row=2, padx=4)
        ttk.Radiobutton(self.mainframe, style='CustomRadiobutton',
        text="Button 2", value=1).grid(column=2, row=2, padx=4)
        ttk.Radiobutton(self.mainframe, style='CustomRadiobutton',
        text="Button 3", value=2).grid(column=3, row=2, padx=4)
        ttk.Radiobutton(self.mainframe, style='CustomRadiobutton',
        text="Button 4", value=3).grid(column=4, row=2, padx=4)
        self.mainloop()

if __name__ == '__main__':
    app = MainGUI()

However, this does not work with all ttk themes. It works with clam and alt, but gives buttons that cannot be selected with classic and default in linux.

Embryology answered 10/1, 2017 at 16:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.