9

To make available an instance of the ttk.Style() class, it was illustrated in this tkinter guide that the syntax is:

import ttk
s=ttk.Style()

When typing these command in IDLE, I noticed that ttk.Style() actually has a predefined argument, i.e.

s=ttk.Style(master=None)

I have written the following test script:

import tkinter as tk
import tkinter.ttk as ttk


class App(ttk.Frame):

    def __init__(self, parent):
        ttk.Frame.__init__(self, parent, style='App.TFrame', relief=tk.SUNKEN,
                           border=10)
        self.parent = parent
        self.__createStyle()
        self.__createWidgets()

    def __createStyle(self):
        self.s = ttk.Style()
        self.s.configure('.', background='orange', border=100)
        self.s.configure('App.TFrame', background='yellow')
        self.s.configure('Btn.TButton', background='light blue', border=10)

    def __createWidgets(self):
        self._label = ttk.Label(self.parent, text='Label packed in root.')
        self._label.pack()
        self._btn = ttk.Button(self, style='Btn.TButton', command=self.__click,
            text='Button packed inside self or class App, which is a ttk.Frame')
        self._btn.pack()

    def __click(self):
        return print('Left Button Clicked!')



class myWidget(ttk.Frame):

    def __init__(self, parent):
        ttk.Frame.__init__(self, parent, style='my.TFrame', relief=tk.GROOVE,
                           border=10)
        self.parent = parent
        self.__createStyle()
        self.__createWidgets()

    def __createStyle(self):
        self.s = ttk.Style()
        self.s.configure('my.TFrame', background='purple')
        self.s.configure('my.TLabel', background='pink', border=10)
        self.s.configure('my.TEntry', foreground='red', border=10)

    def __createWidgets(self):
        self._label = ttk.Label(self, style='my.TLabel',
            text='myWidget Label packed in self or class myWidget, which is a ttk.Frame.')

        self._label.pack()
        self._entry = ttk.Entry(self, style='my.TEntry')
        self._entry.pack()



if __name__ == "__main__":
    root = tk.Tk()
    root.title('Test Style')
    root.geometry('500x150')
    a = App(root)
    a.pack(fill='both', expand=1)
    b = myWidget(a)
    b.pack()

    root.mainloop()

Question 1: When do I need to declare the master arguement in ttk.Style()? E.g. in the above script, if I write self.s = ttk.Style() and self.s = ttk.Style(master=self.parent) in class myWidget, I get the same result (see Fig1).

Question 2: Is there a need to prefix s=ttk.Style() with self? I get the same result as shown in Fig1 with and without the self prefix.

Question 3: If I rename 'my.TFrame' in class myWidget as 'App.TFrame'(this name was used in class App), the background colour of the class App changed to purple color too (same color as class myWidget. Why did this happened given that variable name in different classes are unique?

Question 4: The names 'App.TFrame' and 'my.TFrame' were called before it was declared. Why did python or tkinter not complain or give an error but allowed the script to execute?

Fig1

Figure 1

Fig2

Figure 2

Sun Bear
  • 7,594
  • 11
  • 56
  • 102

2 Answers2

7

When do I need to declare the master arguement in ttk.Style()?

Probably never, except the case when tkinter doesnt support the default root. When you pass None as the master, the master becomes the current root instance of Tk class.

The main purpose of the master (root or any tk-widget) is to delegate instance of tk to the Style, so that the Style could be able to execute Tcl-related commands.

No more, no less.


Is there a need to prefix s=ttk.Style() with self?

It depends on your requirements. In context of your code - self is meaningless, because you're setting up styles in a scope of the __createStyle function.

Otherwise, if you wish to keep the reference, it makes sense to prefix with self.


If I rename my.TFrame in class myWidget as App.TFrame(this name was used in class App), the background colour of the class App changed to purple color too (same color as class myWidget. Why did this happened given that variable name in different classes are unique?

Because both of classes share the same frame style, hence the same color. Created style is a global thing, it can be chaged at runtime, and all the relevant widgets will react to these chages.


The names App.TFrame and my.TFrame were called before it was declared. Why did python or tkinter not complain or give an error but allowed the script to execute?

Why you think they should? When you pass something like <any_sensible_name>.<any_relevant_and_existing_basestyle>, ttk knows that you want a variation of a base style, so it's implicitly creates one, which inherits all base properties.

Try that trick with something more meaningless, like your current style name without dot (ttk.Frame.__init__(..., style='AppTFrame', ...)), which gives you the desired error:

_tkinter.TclError: Layout AppTFrame not found
CommonSense
  • 4,232
  • 2
  • 14
  • 38
  • I think it's worth mentioning that `ttk_frame_widget.winfo_class()` returns `'TFrame'`. – Nae Jan 30 '18 at 16:46
  • Can u elaborate _"When you pass None as the master, the master becomes the current root instance of Tk class."_ using my script? For `class App`, I read `self.s = ttk.Style()` as `ttk.Frame.s` (or `a.s`) is `ttk.Style`. According to `ttk.py`, `ttk.Style()` is to define `self.tk = self.master.tk` where `self.master` is either `tkinter._default_root` or `tkinter.Tk()`. I think `tkinter.Tk()` will create a new root, while `tkinter._default_root` will refer to the root of `a` which is also tkinter.Tk(). For `class myWidget`, it's master is `a` which is a `class App`. So what happens here? – Sun Bear Jan 30 '18 at 18:06
  • @Sun Bear, `ttk.Style().master` could be an anything (Tk, Toplevel, Frame, any widget), that holds reference to `tk`. In your case, you implicitly pass `None` as the `master`, so `master` becomes `tkinter._default_root`, which is your `root`. This easy to check via `print(root is tk._default_root)`. That's it, there's no room for a new instance of `Tk`. – CommonSense Feb 01 '18 at 15:17
2

Only a partial Answer, but I suppose @Bryan Oakley will entlighten us sooner or later.

Question 3:
If you use "App.TFrame" instead of "my.TFrame" inside your MyWidget Class, you override the predefined style properties.

Short example:
If you style "TFrame", all "TFrame"( == Tkinter.Frame/ttk.Frame ) instances will be affected.
This is also sometimes referred to as "root-Style".
If you define another "somename.TFrame" and set it for one Object of type frame, it will be styles according "somename.TFrame".

Question 4:
The lookup names only override default styles.
As long as they have no properties, they do not override a thing.
This "assignment" results in a tcl call and has no specific error handling inside the Tkinter / ttk Sources (used in BaseWidget class).

I can only tell that tcl does not throw an error here but I am not a tcl expert myself.

I hope this at least helps a bit.

R4PH43L
  • 2,122
  • 3
  • 18
  • 30