3

I need to create a custom style for button widgets which has the same appearance as buttons using the ttk 'clam' theme.

I can set the theme like:

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

However, given the nature of a theme, this will then set all ttk widgets to use 'clam'.

I would like to be able to set certain ttk buttons to use the clam appearance and others to use default ttk.

I have tried looking at the layouts and configurations of 'TButton' whilst the clam theme is in use but it seems that a theme is a collection of styles and I am unsure on how to 'map' a custom style based on the clam button style.

stovfl
  • 14,998
  • 7
  • 24
  • 51
Luke.py
  • 965
  • 8
  • 17

1 Answers1

4

using this code:

import Tkinter as tk
import ttk

def get_element_details(style):
    print('element: %s' % style)
    print('option: %s' % str(s.element_options(style)))
    layout = s.layout(style)
    for elem, elem_dict in layout:
        get_sub_element_details(elem, elem_dict)
    print(layout)

def get_sub_element_details(elem, _dict, depth=1):
    print('%selement: %s' % (''.join(['\t' for i in range(depth)]), elem))
    for key in _dict:
        if key != 'children':
            print('%s%s: %s' % (''.join(['\t' for i in range(depth+1)]), key, _dict[key]))
    print('%soption: %s' % (''.join(['\t' for i in range(depth+1)]), s.element_options(elem)))
    if 'children' in _dict:
        for child, child_dict in _dict['children']:
            get_sub_element_details(child, child_dict, depth+1)

root = tk.Tk()
widget = ttk.Button(root, text='test')
widget.grid(sticky='nesw')

style = widget.winfo_class()

s = ttk.Style()

print(s.theme_use())
print('normal theme')
get_element_details(style)

print('\nclam theme')
s.theme_use('clam')
get_element_details(style)

you can egt details about all the layout and config options of the widget. with the native theme on my box (xp) i get this output:

element: TButton
option: ()
    element: Button.button
        sticky: nswe
        option: ()
        element: Button.focus
            sticky: nswe
            option: ()
            element: Button.padding
                sticky: nswe
                option: ('-padding', '-relief', '-shiftrelief')
                element: Button.label
                    sticky: nswe
                    option: ('-compound', '-space', '-text', '-font', '-foreground', '-underline', '-width', '-anchor', '-justify', '-wraplength', '-embossed', '-image', '-stipple', '-background')

and with the clam theme i get:

element: TButton
option: ()
    element: Button.border
        border: 1
        sticky: nswe
        option: ('-bordercolor', '-lightcolor', '-darkcolor', '-relief', '-borderwidth')
        element: Button.focus
            sticky: nswe
            option: ('-focuscolor', '-focusthickness')
            element: Button.padding
                sticky: nswe
                option: ('-padding', '-relief', '-shiftrelief')
                element: Button.label
                    sticky: nswe
                    option: ('-compound', '-space', '-text', '-font', '-foreground', '-underline', '-width', '-anchor', '-justify', '-wraplength', '-embossed', '-image', '-stipple', '-background')

note that the clam theme has a Button.border element with options, where the native theme has a Button.button element with no options.

you can save the layout from the clam theme (either at write time, or you can get it during run time by loading clam theme, fetch layout then switch theme back and load the layout back in) and use that to style the button.

EDIT in theory this should work:

import Tkinter as tk
import ttk

root = tk.Tk()

style = 'TButton'

s = ttk.Style()

#s.theme_use('clam')

#get_element_details(style)

clambuttonlayout = [('Button.border', {'border': '1', 'children': [('Button.focus', {'children': [('Button.padding', {'children': [('Button.label', {'sticky': 'nswe'})], 'sticky': 'nswe'})], 'sticky': 'nswe'})], 'sticky': 'nswe'})]

s.layout('clam.TButton', clambuttonlayout)

b1 = ttk.Button(root, text="Button 1", style='clam.TButton')
b1.grid()
b2 = ttk.Button(root, text="Button 2", style='TButton')
b2.grid()

root.mainloop()

however for some reason when I do this the text no longer appears on the first button... if i figure it out i'll edit again.

James Kent
  • 5,763
  • 26
  • 50
  • Hi James. Thanks for your answer! Im assuming you are using Python x3 looking at your print statements? When I run your code I get the following error: `print '%soption: %s' % (''.join(['\t' for s in range(depth+1)]), s.element_options(elem)) AttributeError: 'int' object has no attribute 'element_options'` - Could this be due to Py 3x or a different version of Tkinter>? – Luke.py May 25 '17 at 14:17
  • sorry, thats a python2 vs python3 difference, using s as the index overwrote the style object inside the function, i've changed it to i now, and tested in python2.7 – James Kent May 25 '17 at 14:22
  • Thanks James thats really cool! - could you elaborate how to manually asign buttons a style using the output from above? – Luke.py May 25 '17 at 15:03