pythontkinterttk

How to get all Styling parameter configurable by `ttk.Style().configure()` for a widget from Themed Tkinter?


I have been searching the answer for this question from a long time but with no success had to ask it here. I am able to get the styling parameter for from the tcl documentation, but my question is how can I achieve the same result programmatically.

For example in Tkinter, we can use widget.configure() with no parameters to get all valid parameters for that widgets, since all design parameter must be changed using Style() only in themed tkinter, how can achieve the same functionality?

Edit Consider this example:

import tkinter as tk
root =tk.Tk()
a = tk.Label(root)
print(a.configure())

#Output
{'activebackground': ('activebackground', 'activeBackground', 'Foreground', <string object: 'SystemButtonFace'>, 'SystemButtonFace'), 'activeforeground': ('activeforeground', 'activeForeground', 'Background', <string object: 'SystemButtonText'>, 'SystemButtonText'), 'anchor': ('anchor', 'anchor', 'Anchor', <string object: 'center'>, 'center'), 'background': ('background', 'background', 'Background', <string object: 'SystemButtonFace'>, 'SystemButtonFace'), 'bd': ('bd', '-borderwidth'), 'bg': ('bg', '-background'), 'bitmap': ('bitmap', 'bitmap', 'Bitmap', '', ''), 'borderwidth': ('borderwidth', 'borderWidth', 'BorderWidth', <string object: '2'>, <string object: '2'>), 'compound': ('compound', 'compound', 'Compound', <string object: 'none'>, 'none'), 'cursor': ('cursor', 'cursor', 'Cursor', '', ''), 'disabledforeground': ('disabledforeground', 'disabledForeground', 'DisabledForeground', <string object: 'SystemDisabledText'>, 'SystemDisabledText'), 'fg': ('fg', '-foreground'), 'font': ('font', 'font', 'Font', <string object: 'TkDefaultFont'>, 'TkDefaultFont'), 'foreground': ('foreground', 'foreground', 'Foreground', <string object: 'SystemButtonText'>, 'SystemButtonText'), 'height': ('height', 'height', 'Height', 0, 0), 'highlightbackground': ('highlightbackground', 'highlightBackground', 'HighlightBackground', <string object: 'SystemButtonFace'>, 'SystemButtonFace'), 'highlightcolor': ('highlightcolor', 'highlightColor', 'HighlightColor', <string object: 'SystemWindowFrame'>, 'SystemWindowFrame'), 'highlightthickness': ('highlightthickness', 'highlightThickness', 'HighlightThickness', <string object: '0'>, <string object: '0'>), 'image': ('image', 'image', 'Image', '', ''), 'justify': ('justify', 'justify', 'Justify', <string object: 'center'>, 'center'), 'padx': ('padx', 'padX', 'Pad', <string object: '1'>, <string object: '1'>), 'pady': ('pady', 'padY', 'Pad', <string object: '1'>, <string object: '1'>), 'relief': ('relief', 'relief', 'Relief', <string object: 'flat'>, 'flat'), 'state': ('state', 'state', 'State', <string object: 'normal'>, 'normal'), 'takefocus': ('takefocus', 'takeFocus', 'TakeFocus', '0', '0'), 'text': ('text', 'text', 'Text', '', ''), 'textvariable': ('textvariable', 'textVariable', 'Variable', '', ''), 'underline': ('underline', 'underline', 'Underline', -1, -1), 'width': ('width', 'width', 'Width', 0, 0), 'wraplength': ('wraplength', 'wrapLength', 'WrapLength', <string object: '0'>, <string object: '0'>)}

but

import tkinter.ttk as ttk

b= ttk.Label(root, text="hello")
print(ttk.Style(root).configure("TLabel")

#Output: //Nothing

Image from Tcl Official Docs Tcl Doc page screenshot

Hence, I would like to get all the styling options configurable with ttk::style for a particular widget via python program.

Note:

This question differs from similar question asked previously on StackOverflow such as How to know all style options of a ttk widget? in sense that it asks about the options of all element within a widget, But this question is specfically about the options which if passed into style.configure("Widget", options) are valid and have effect to the apperacnce of the widget.


Solution

  • After reading the source code I think I got some ideas. The problem why you can't get the rowheight and indent options for your treeview is that these options are not attached to any elements at all. They are only stored in some special so-called option table (which I think is a "hashtable"). But in order to actually store them in this table, tkinter or tk/tcl commands have to be called first (e.g. style.configure(rowheight=30)). Before that, they simply don't exist anywhere (I am quite sure about that, but not 100%). So unfortunately you can't access them programmatically in the "normal" way. This is also true for element options, they are also quite often not stored at the beginning of the program in an "option table" (that is why style.configure({Theme}) quite often returns an empty dictionary, all option tables are empty at the beginning of the program).

    But you can get the options for each element because the developers actually implemented special methods and stored the options inside the theme files in arrays so you can just get them. But they didn't make the same methods for these special widget options.

    I think it might be some kind of "bug". But most likely they just felt there was no need to do this functionality, since all these "widget-specific" options can be found in their documentation. These options, such as "rowheight", are also theme-independent, so they can always be called, regardless of the theme.

    But if it really needs to be obtained by programming, the only way I can see to get them is to actually search through the ttk "C" widget files (e.g. for treeview the file is "ttkTreeview.c") and look for the functions that are used to get the options from the option data table. For example, in the "ttkTreeview.c" file there is code like this:

    if ((objPtr = Ttk_QueryOption(treeLayout, "-rowheight", 0))) {
        (void)Tcl_GetIntFromObj(NULL, objPtr, &tv->tree.rowHeight);
        tv->tree.rowHeight = MAX(tv->tree.rowHeight, 1);
    }
    if ((objPtr = Ttk_QueryOption(treeLayout, "-indent", 0))) {
        (void)Tcl_GetIntFromObj(NULL, objPtr, &tv->tree.indent);
    }
    

    The "C" method Ttk_QueryOption is used to get an option from the options data table (I think it is roughly equivalent to the tkinter style.lookup() method). So what you can do is search the file for the Ttk_QueryOption method and get these additional options that you can't get from the element_options() method. But obviously this is not a good approach, just a workaround. And it is impossible to be sure that you can perform this search. First, Ttk_QueryOption may not be the only method used to get the option from the options tables. Second, it may be wrapped around another method, like this (roughly equivalent in Python):

    def some_random_name(style, option, state, default):
        return ttk.Style().lookup(style, option, state, default)
    

    Conclusion (and some additional information): There is no built-in method (even at C level) to get these special widget options. And probably the only way to get them is to look for them in the widget "C" files.

    Also, something I have not mentioned yet. There is this thing called "sublayouts". What these are is sort of additional sub "Layouts" (styles) for ttk widgets. For example, the main style name for the ttk.Treeview widget is "Treeview", but it also has sublayouts (which are mentioned in the documentation) "Heading", "Item", "Cell", and "Row" (which for some reason isn't mentioned). So there are also options for Treeview that exist for these sublayouts. For example, for the "clam" theme, the layout and parameters of "Treeview.Item" are as follows: {'Treeitem.padding': ('padding', 'relief', 'shiftrelief'), 'Treeitem.indicator': ('foreground', 'indicatorsize', 'indicatormargins'), 'Treeitem.image': ('image', 'stipple', 'background'), 'Treeitem.text': 'text', 'font', 'foreground', 'underline', 'width', 'anchor', 'justify', 'wraplength', 'embossed')}

    And… you can't get sublayers either. So if you really want to, play with the tk source files. The "ttkTreeview.c" file also mentions sublayers:

    treeLayout
         && GetSublayout(interp, themePtr, treeLayout, ".Item",
            tv->tree.tagOptionTable, &tv->tree.itemLayout)
         && GetSublayout(interp, themePtr, treeLayout, ".Cell",
            tv->tree.tagOptionTable, &tv->tree.cellLayout)
         && GetSublayout(interp, themePtr, treeLayout, ".Heading",
            tv->tree.headingOptionTable, &tv->tree.headingLayout)
         && GetSublayout(interp, themePtr, treeLayout, ".Row",
            tv->tree.tagOptionTable, &tv->tree.rowLayout)
    

    But honestly, it would probably be much easier to do it yourself, without coding, just by looking at the widget source files to get all this information. As for the element options, you are more than welcome to use the element_options() command.

    Note: I would also recommend reading this documentation. It covers some pretty advanced information about styles towards the end.