Search code examples
pythontkinterttk

tkinter: How to apply customized ttk stylename that is defined in one class to other classes


I would like to apply customized stylenames defined in a class to widgets found in other classes. How can I implement this?

Below is a test script for consideration. I would like to apply stylenames 'page.TFrame' and 'press.TButton' to classes Page1 and Page2

Test script:

#!/usr/bin/python3
# -*- coding: utf-8 -*-

# tkinter modules
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.font as tkFont


class App(ttk.Frame):


    def __init__(self, parent=None, *args, **options):
        # Extract key and value from **options using Python3 "pop" function:
        #   pop(key[, default])
        self.style = options.pop('style',ttk.Style())
        self.bg0   = options.pop('background1','light grey')
        self.bg1   = options.pop('background2','grey')
        self.fg0   = options.pop('fg_link','black')
        self.fg1   = options.pop('fg_text','white')
        self.font  = options.pop('font',tkFont.Font(family='Nimbus Roman No9 L',
                                 size='10', weight='normal'))

        # Initialise App Frame
        ttk.Frame.__init__(self, parent, style='self.TFrame')
        self.__setFont()
        self.__setStyle()
        self.__createPages(parent)


    def __setFont(self):
        """Set the fonts to be used to build the App GUI."""
        self.fontTitle = self.font.copy()
        self.fontTitle.config(size='30')
        self.font.config(size='13')


    def __setStyle(self):
        """Customise ttk styles""" 
        ## LoginPanel styles
        self.style.configure('self.TFrame', background='pink')
        self.style.configure('page.TFrame', background=self.bg0)
        self.style.configure('press.TButton', font=self.fontTitle,
                             background=self.bg0, foreground=self.fg0,
                             relief='raised')


    def __createPages(self, parent):
        """Create Login and NewUser pages."""
        self._frames = {}
        for F in (Page1, Page2):
            page_name = F.__name__
            frame = F(parent=self)
            self._frames[page_name] = frame
            frame.grid(row=0, column=0, sticky="nsew")

        self.showFrame("Page1")

        parent.rowconfigure(0, weight=1)
        parent.columnconfigure(0, weight=1)


    def showFrame(self, page_name):
        '''Show a frame for the given page name'''
        frame = self._frames[page_name]
        frame.tkraise()



class Page1(ttk.Frame):

    def __init__(self, parent):
        ttk.Frame.__init__(self, parent, style='What to write here?')

        b1 = ttk.Button(self, text='Page 1',,
                        style='What to write here?')
        b1 .grid(row=0, column=0, sticky='nsew')

        b2 = ttk.Button(self, text='Goto Page 2?',
                        command=lambda: parent.showFrame("Page2"))
        b2 .grid(row=1, column=0, sticky='nsew')


class Page2(ttk.Frame):

    def __init__(self, parent):
        ttk.Frame.__init__(self, parent, style='What to write here?')

        b1 = ttk.Button(self, text='Page 2',
                        style='What to write here?')
        b1.grid(row=0, column=0, sticky='nsew')

        b2 = ttk.Button(self, text='Goto Page 1?',
                        command=lambda: parent.showFrame("Page1"))
        b2 .grid(row=1, column=0, sticky='nsew')


if __name__ == '__main__':
    root = tk.Tk()
    root.geometry('400x350+300+300')
    BG1 = 'blue'
    BG0 = 'lightblue'
    FG0 = 'white'
    FG1 = 'light blue'

    app = App(root, background1=BG0, background2=BG1,
                  fg_link=FG1, fg_text=FG0)
    app.grid(row=0, column=0, sticky='nsew')

    root.rowconfigure(0, weight=1)
    root.columnconfigure(0, weight=1)

    root.mainloop()

Solution

  • I finally figured out and surprisingly the answer is a simple one.

    Answer: I just have to pass the style attribute parameter containing the customized style names that I want to use to the other classes that needs to use those style names. In my script, the attribute parameter that needs to be passed is self.style. Hope this answer helps others with the same need.

    #!/usr/bin/python3
    # -*- coding: utf-8 -*-
    
    # tkinter modules
    import tkinter as tk
    import tkinter.ttk as ttk
    import tkinter.font as tkFont
    
    
    class App(ttk.Frame):
    
    
        def __init__(self, parent=None, *args, **options):
            # Extract key and value from **options using Python3 "pop" function:
            #   pop(key[, default])
            self.style = options.pop('style',ttk.Style())
            self.bg0   = options.pop('background1','light grey')
            self.bg1   = options.pop('background2','grey')
            self.fg0   = options.pop('fg_link','black')
            self.fg1   = options.pop('fg_text','white')
            self.font  = options.pop('font',tkFont.Font(family='Nimbus Roman No9 L',
                                     size='10', weight='normal'))
    
            # Initialise App Frame
            ttk.Frame.__init__(self, parent, style='self.TFrame')
            self.__setFont()
            self.__setStyle()
            self.__createPages(parent)
    
    
        def __setFont(self):
            """Set the fonts to be used to build the App GUI."""
            self.fontTitle = self.font.copy()
            self.fontTitle.config(size='30')
            self.font.config(size='13')
    
    
        def __setStyle(self):
            """Customise ttk styles""" 
            ## LoginPanel styles
            self.style.configure('self.TFrame', background='pink')
            self.style.configure('page.TFrame', background=self.bg0)
            self.style.configure('press.TButton', font=self.fontTitle,
                                 background=self.bg1, foreground=self.fg0,
                                 relief='raised')
    
    
        def __createPages(self, parent):
            """Create Login and NewUser pages."""
            self._frames = {}
            for F in (Page1, Page2):
                page_name = F.__name__
                frame = F(parent=self, style=self.style) #pass style attribute
                self._frames[page_name] = frame
                frame.grid(row=0, column=0, sticky="nsew")
    
            self.showFrame("Page1")
    
            parent.rowconfigure(0, weight=1)
            parent.columnconfigure(0, weight=1)
    
    
        def showFrame(self, page_name):
            '''Show a frame for the given page name'''
            frame = self._frames[page_name]
            frame.tkraise()
    
    
    
    class Page1(ttk.Frame):
    
        def __init__(self, parent, style): #added `style` argument
            ttk.Frame.__init__(self, parent, style='page.TFrame') #call the stylename
    
            b1 = ttk.Button(self, text='Page 1', style='press.TButton') #call the stylename
            b1 .grid(row=0, column=0, sticky='nsew')
    
            b2 = ttk.Button(self, text='Goto Page 2?',
                            command=lambda: parent.showFrame("Page2"))
            b2 .grid(row=1, column=0, sticky='nsew')
    
    
    class Page2(ttk.Frame):
    
        def __init__(self, parent, style): #added `style` argument
            ttk.Frame.__init__(self, parent, style='page.TFrame') #call the stylename
    
            b1 = ttk.Button(self, text='Page 2', style='press.TButton') #call the stylename
            b1.grid(row=0, column=0, sticky='nsew')
    
            b2 = ttk.Button(self, text='Goto Page 1?',
                            command=lambda: parent.showFrame("Page1"))
            b2 .grid(row=1, column=0, sticky='nsew')
    
    
    if __name__ == '__main__':
        root = tk.Tk()
        root.geometry('400x350+300+300')
        BG1 = 'blue'
        BG0 = 'lightblue'
        FG0 = 'white'
        FG1 = 'light blue'
    
        app = App(root, background1=BG0, background2=BG1,
                      fg_link=FG1, fg_text=FG0)
        app.grid(row=0, column=0, sticky='nsew')
    
        root.rowconfigure(0, weight=1)
        root.columnconfigure(0, weight=1)
    
        root.mainloop()