Search code examples
pythontkinterscrollbarttk

How to change the option of an element in a *ttk stylename* without affecting the same option found in other elements?


How to change the option of an element in a ttk stylename without affecting the same option found in other elements?

I have posted a script here where a tkinter programmer can use it to expose all elements, and their options, concerning a given ttk stylename. E.g. for stylename my.Vertical.TScrollbar, you can see that this stylename has 3 elements each having a similar option called background.

Stylename = my.Vertical.TScrollbar
Layout    = [('Vertical.Scrollbar.trough', {'children': [('Vertical.Scrollbar.uparrow', {'side': 'top', 'sticky': ''}), ('Vertical.Scrollbar.downarrow', {'side': 'bottom', 'sticky': ''}), ('Vertical.Scrollbar.thumb', {'sticky': 'nswe', 'expand': '1'})], 'sticky': 'ns'})]

Element(s) = ['Vertical.Scrollbar.trough', 'Vertical.Scrollbar.uparrow', 'Vertical.Scrollbar.downarrow', 'Vertical.Scrollbar.thumb']

Vertical.Scrollbar.trough      options: ('-borderwidth', '-troughcolor', '-troughrelief')
Vertical.Scrollbar.uparrow     options: ('-background', '-relief', '-borderwidth', '-arrowcolor', '-arrowsize')
Vertical.Scrollbar.downarrow   options: ('-background', '-relief', '-borderwidth', '-arrowcolor', '-arrowsize')
Vertical.Scrollbar.thumb       options: ('-orient', '-width', '-relief', '-background', '-borderwidth')

tkinter documentation teaches that the option(s) of the a stylename can be changed using the .configure method. Below is a script illustrating the issue. When I issue the command

style.configure('my.Vertical.TScrollbar',
                background=color1, troughcolor=color2,
                arrowcolor=color3, borderwidth=10, width=40)

the background of elements thumb, uparrow and downarrow would all change to blue color, and the borderwidth of all 4 elements become 10 pixel wide. If I require finer control, e.g. to change the background of only the thumb element or to change the borderwidth of only the thumb element, how can I do this?

Test Script:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
import tkinter as tk
import tkinter.ttk as ttk

class App(ttk.Frame):

    def __init__(self, parent=None, *args, **kwargs):
        ttk.Frame.__init__(self, parent, *args, **kwargs)

        color1 = 'blue'
        color2 = 'light green'
        color3 = 'white'

        style=ttk.Style()
        style.configure('my.Vertical.TScrollbar',
                        background=color1, troughcolor=color2,
                        arrowcolor=color3, borderwidth=10, width=40)
        style.map('my.Vertical.TScrollbar',
            background=[('active',color1),('!active',color1)],
            arrowcolor=[('active',color3),('!active',color3)])

        vscrollbar = ttk.Scrollbar(self,
                                   orient='vertical',
                                   style='my.Vertical.TScrollbar')
        vscrollbar.pack(fill='y', side='right', expand='false')
        canvas = tk.Canvas(self,
                           bd=0,
                           highlightthickness=0,
                           yscrollcommand=vscrollbar.set)
        canvas.pack(side='left', fill='both', expand='true')
        vscrollbar.config(command=canvas.yview)


if __name__ == "__main__":
    root=tk.Tk()
    app = App(root)
    app.pack(side='left', fill='both', expand='true')
    root.mainloop()

enter image description here


Solution

  • The elements in ttk use the flyweight pattern and do not store values themselves. Instead the style contains all the values used by the elements associated with the widget layout. So all elements that use a background configuration value read the same value out of the current widget style. In cases where a custom color is supported a different option name is used. Hence the trough background color is read from the troughcolor option. For buttons there is a field for the button itself as background gets used for the area behind the button under the focus ring (if visible).

    If you really wanted to get finer control you either have to code a new theme element engine or you can use the image element engine to define a new element using images. There are some tcl examples of image based themes imported to github if you want to try that. However, the point of the ttk themeing is to try and use the system provided look and feel as much as possible and avoid having programmers customise the heck out of the UI. The more customisation you make the more likely it is to look crap on someone else's system as a general rule.