This problem nearly makes me craze. I am a new beginner and without knowledge of tck/tk. I have done carefully search on the internet but haven\'t found a good solution.
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.
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
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')