Search code examples
pythonbuttontkinterttk

How to create arrow in a tkinter.ttk.Button and control it's size?


I would like to create ttk.button with an arrow in it and have the arrow size changable.

I discovered 'TButton' inherently contains StyleNames TButton.leftarrow which is not exposed by the ttk.Style().layout().

Questions: (1) How do I activate these StyleNames? (2) How do I control the size of .leftarrow? I notice it has a arrowsize option . How do I use it?

import tkinter as tk
import tkinter.ttk as ttk

class App(ttk.Frame):

        def __init__(self, parent):
            ttk.Frame.__init__(self, parent)
            self.parent = parent
            self.setStyle()
            self.setWidget()

        def setStyle(self):
            style = ttk.Style()
            print('Left.TButton layout are:', style.layout('Left.TButton'))
            print("Left.TButton.leftarrow style.element_options: ",
                  style.element_options('Left.TButton.leftarrow'))

            style.configure('Left.TButton', background='yellow')
            style.configure('Left.TButton.leftarrow', background='white',
                            arrowsize=20)

        def setWidget(self):
            self.lbutton = ttk.Button(self.parent, style='Left.TButton')
            self.lbutton2 = ttk.Button(self.parent, style='Left.TButton.leftarrow')
            self.lbutton.pack()
            self.lbutton2.pack()


    if __name__ == '__main__':
        root = tk.Tk()
        root.title('Test')
        root.geometry('200x50')
        app = App(root)
        app.pack(expand=1, fill='both')

Solution

  • After quite a lot of attempts and closer studies of the ttk documentations, I discovered the following:

    1. To create a arrow in the button, I have to declare a arrow element as the child of the focus element in the layout of the custom style that is to be used for the ttk.Button() widget. To do this, I needed to use the ttk.Style().layout() method.
    2. The size of the arrow is dependent on the font size of the label element. Therefore, a label element had to be declared in layout of the style TButton. The arrowsize option of the arrowleft element did not seem to work. I have commented out this line of code which did not work. However, the arrowcolor option of the leftarrow element does work. To adjust the label element's font size, the ttk.Style().configuration method was used.

    Approach 2 in Test Script demonstrates the solution to my question.

    Arrow Button

    Test Code:

    import tkinter as tk
    import tkinter.ttk as ttk
    
    
    class App(ttk.Frame):
    
        def __init__(self, parent):
            ttk.Frame.__init__(self, parent)
            self.parent = parent
            self.setStyle()
            self.setWidget()
    
        def setStyle(self):
            style = ttk.Style()
            print('\nDefault TButton layout:')
            print(style.layout('TButton'))
    
            print ('\nTButton Elements and their options:')
            print("border options: ", style.element_options('Button.border'))
            print("focus options: ",  style.element_options('Button.focus'))
            print("padding options: ",style.element_options('Button.padding'))
            print("label options: ",  style.element_options('Button.label'))
            print("arrow options: ",  style.element_options('Button.arrow'))
    
            print ('\nElement TButton.label and its options:')
            print("compound: ",  style.lookup('Button.label', 'compound'))
            print("space: ",     style.lookup('Button.label', 'space'))
            print("text: ",      style.lookup('Button.label', 'text'))
            print("font: ",      style.lookup('Button.label', 'font'))
            print("foreground: ",style.lookup('Button.label', 'foreground'))
            print("underline: ", style.lookup('Button.label', 'underline'))
            print("width: ",     style.lookup('Button.label', 'width'))
            print("anchor: ",    style.lookup('Button.label', 'anchor'))
            print("justify: ",   style.lookup('Button.label', 'justify'))
            print("wraplength: ",style.lookup('Button.label', 'wraplength'))
            print("embossed: ",  style.lookup('Button.label', 'embossed'))
            print("image: ",     style.lookup('Button.label', 'image'))
            print("stipple: ",   style.lookup('Button.label', 'stipple'))
            print("background: ",style.lookup('Button.label', 'background'))
    
            print ('\nElement TButton.arrow and its options:')
            print("background: ", style.lookup('Button.arrow', 'background'))
            print("relief: ",     style.lookup('Button.arrow', 'relief'))
            print("borderwidth: ",style.lookup('Button.arrow', 'borderwidth'))
            print("arrowcolor: ", style.lookup('Button.arrow', 'arrowcolor'))
            print("arrowsize: ",  style.lookup('Button.arrow', 'arrowsize'))
    
            #Define style Default.TButton with yellow background
            style.configure('Default.TButton', background='yellow')
            #Change the 2 options of the "label" element in its style's layout  
            style.configure('Default.TButton.label', foreground='red')
            style.configure('Default.TButton.label', borderwidth=20)
            print ('\nElement Default.TButton.label and its options (after configuration):')
            print("background: ",  style.lookup('Default.TButton.border', 'background'))
            print("borderwidth: ", style.lookup('Default.TButton.border', 'borderwidth'))
    
            #Approach 1
            #==========
            # Define style Left.TButton to show the following elements: leftarrow,
            #  padding, label 
            style.layout(
                'Left1.TButton',[
                    ('Button.focus', {'children': [
                        ('Button.leftarrow', None),
                        ('Button.padding', {'sticky': 'nswe', 'children': [
                            ('Button.label', {'sticky': 'nswe'}
                             )]}
                         )]}
                     )]
                )
            #Change 3 options of the "arrow" element in style "Left.TButton"
            style.configure('Left1.TButton.leftarrow',
                            background='white',
                            borderwidth=10,
                            arrowsize=20)
            print('\nElement TButton.arrow and its options (after changing):')
            print("background: ",  style.lookup('Left1.TButton.arrow','background'))
            print("borderwidth: ", style.lookup('Left1.TButton.arrow','borderwidth'))
            print("arrowsize: ",   style.lookup('Left1.TButton.arrow','arrowsize'))
    
            #Approach 2
            #==========
            style.layout(
                'Left2.TButton',[
                    ('Button.focus', {'children': [
                        ('Button.leftarrow', None),
                        ('Button.padding', {'sticky': 'nswe', 'children': [
                            ('Button.label', {'sticky': 'nswe'}
                             )]}
                         )]}
                     )]
                )
    
            style.configure('Left2.TButton',font=('','20','bold'), width=1, arrowcolor='white')
            #style.configure('Left2.TButton', width=1, arrowcolor='white', arrowsize='20')
            #option arrowsize does not work
    
    
        def setWidget(self):
            self.lbutton = ttk.Button(self.parent, style='Default.TButton',
                                      text='Default.TButton')
            self.lbutton1 = ttk.Button(self.parent, style='Left1.TButton',
                                       text='Left1.Button')
            self.lbutton2 = ttk.Button(self.parent, style='Left2.TButton',
                                       text='')
            self.lbutton.pack()
            self.lbutton1.pack()
            self.lbutton2.pack()
    
    
    if __name__ == '__main__':
        root = tk.Tk()
        root.title('Test')
        root.geometry('200x100')
        app = App(root)
        app.pack(expand=1, fill='both')