26

I am without knowledge of tck/tk. I have done carefully search on the internet but haven't found a good solution.

For example, I created a LabelFrame using:

import tkinter as tk
from tkinter import ttk
newBT = ttk.LabelFrame(width=100, height=100)

Then I need to set the frame style. There is foreground for tk.LabelFrame. However, I didn't find such style option for ttk.LabelFrame on NMT and tck/tk reference. Then I have to guess, like following

s = ttk.Style()
s.configure('TLabelframe', foreground='red')

But this doesn't work, the right thing is:

s.configure('TLabelframe.Label', foreground='red')

So, my question is, how can I find out all the style options a ttk widget has. Is there some function like:

s.getAllOptions('TLabelframe')

and then the output is something like:

['background', 'foreground', 'padding', 'border', ...]
martineau
  • 119,623
  • 25
  • 170
  • 301
Nan Zhou
  • 1,205
  • 1
  • 13
  • 14
  • 1
    I think I find a solution. First, use `s.layout('TLabelframe')` to find out all elements of this widget. Second, use `s.element_options('element name')` to find out all available options. Unfortunately, I haven't found out how to get **TLabelframe.Label** because `s.layout('TLabelframe')` doesn't output this. It seems not an element of ttk.Labelframe. – Nan Zhou Jul 30 '17 at 11:04
  • 1
    I'm looking for something similar. It seems like one could use `widget.configure()` call, without parameters and get a list of properties in which you cand find the style with its name. Using the name style you can create a tk style and retrieve its color. I'm going to try that way – madtyn Oct 07 '17 at 08:06
  • 2
    Thanks madtyn. You are right the .configure() without argument would tell you properties and current settings. However, that won't give you the style you can set. – Nan Zhou Oct 09 '17 at 05:52
  • I'm still working for an answer. I have plenty of this done. – madtyn Oct 09 '17 at 09:55

3 Answers3

22

I found your question interesting as I had asked myself the same question but have not found time to address it until now. I have written a function called stylename_elements_options(stylename) to do just this. Sharing it here. Hope it can benefit you (although it is 6 months late) and any tkinter users asking the same question.

Script:

import tkinter as tk
import tkinter.ttk as ttk

def stylename_elements_options(stylename):
    '''Function to expose the options of every element associated to a widget
       stylename.'''
    try:
        # Get widget elements
        style = ttk.Style()
        layout = str(style.layout(stylename))
        print('Stylename = {}'.format(stylename))
        print('Layout    = {}'.format(layout))
        elements=[]
        for n, x in enumerate(layout):
            if x=='(':
                element=""
                for y in layout[n+2:]:
                    if y != ',':
                        element=element+str(y)
                    else:
                        elements.append(element[:-1])
                        break
        print('\nElement(s) = {}\n'.format(elements))

        # Get options of widget elements
        for element in elements:
            print('{0:30} options: {1}'.format(
                element, style.element_options(element)))

    except tk.TclError:
        print('_tkinter.TclError: "{0}" in function'
              'widget_elements_options({0}) is not a regonised stylename.'
              .format(stylename))

stylename_elements_options('my.Vertical.TScrollbar')
Sun Bear
  • 7,594
  • 11
  • 56
  • 102
  • Thanks this is really helpful – Nan Zhou Mar 09 '18 at 05:59
  • You are a hero for this answer! Very helpful, thanks! – jda5 Jun 26 '20 at 09:29
  • Thanks for your answer, its really helpful. I want to add some thing, for every them the elements are different. if you use `style.theme_use('default')` you will get different Element list and for other theme you will get a different – Faraaz Kurawle Jan 20 '22 at 12:30
12

The issue is that if you really want to control a style in detail you need to use the layout. So first identify the widget class using:

>>b=ttk.Button(None)
>>b.winfo_class()
'TButton

Then use the command

>>> s.layout('TButton')
[("Button.border", {"children": [("Button.focus", {"children": 
[("Button.spacing",
{"children": [("Button.label", {"sticky": "nswe"})], "sticky": "nswe"})], 
"sticky": "nswe"})], "sticky": "nswe", "border": "1"})] 

Finally change what you want:

s.layout("MYButton.TButton",[("Button.border", {"children": 
[("Button.focus", {"children": [("Button.spacing", {"children": 
[("Button.label", {"sticky": "nswe"})], "sticky": "nswe"})], "sticky": 
"nswe"})], "sticky": "we", "border": "1"})]

This made the trick for me and finally provides me a way to control my ttk widget!!!

Luca

LucaG
  • 341
  • 3
  • 11
  • This is a good answer to my question. BTW, with `s.element_options()` you can see what options you can set for a layout element. – Nan Zhou Oct 22 '17 at 22:12
11

Building on SunBear's script:

import tkinter as tk
import tkinter.ttk as ttk

def iter_layout(layout, tab_amnt=0, elements=[]):
    """Recursively prints the layout children."""
    el_tabs = '  '*tab_amnt
    val_tabs = '  '*(tab_amnt + 1)

    for element, child in layout:
        elements.append(element)
        print(el_tabs+ '\'{}\': {}'.format(element, '{'))
        for key, value in child.items():
            if type(value) == str:
                print(val_tabs + '\'{}\' : \'{}\','.format(key, value))
            else:
                print(val_tabs + '\'{}\' : [('.format(key))
                iter_layout(value, tab_amnt=tab_amnt+3)
                print(val_tabs + ')]')

        print(el_tabs + '{}{}'.format('} // ', element))

    return elements

def stylename_elements_options(stylename, widget):
    """Function to expose the options of every element associated to a widget stylename."""

    try:
        # Get widget elements
        style = ttk.Style()
        layout = style.layout(stylename)
        config = widget.configure()

        print('{:*^50}\n'.format(f'Style = {stylename}'))

        print('{:*^50}'.format('Config'))
        for key, value in config.items():
            print('{:<15}{:^10}{}'.format(key, '=>', value))

        print('\n{:*^50}'.format('Layout'))
        elements = iter_layout(layout)

        # Get options of widget elements
        print('\n{:*^50}'.format('element options'))
        for element in elements:
            print('{0:30} options: {1}'.format(
                element, style.element_options(element)))

    except tk.TclError:
        print('_tkinter.TclError: "{0}" in function'
                'widget_elements_options({0}) is not a regonised stylename.'
                .format(stylename))

widget = ttk.Button(None)
class_ = widget.winfo_class()
stylename_elements_options(class_, widget)

Prints the config options as well as the layout tree.

Thom
  • 1,473
  • 2
  • 20
  • 30
Thomas
  • 111
  • 1
  • 2