5 votes

Change color of selected tab in ttk.Notebook

I am creating an application in Python 3.6 that has several tabs and I am using the widget Notebook of Tkinter. What I want is that when selecting one of the tabs it is clearly appreciated which one is selected by changing the color of the tab.

By default it looks a whiter shade but it is not enough, here is an example of what I am trying to say:

import tkinter
from tkinter import ttk

root = tkinter.Tk()

nb = ttk.Notebook(width=200, height=200)
nb.pressed_index = None
f1 = tkinter.Frame(nb, background="red")
f2 = tkinter.Frame(nb, background="green")
f3 = tkinter.Frame(nb, background="blue")
nb.add(f1, text='Red', padding=3)
nb.add(f2, text='Green', padding=3)
nb.add(f3, text='Blue', padding=3)
nb.pack(expand=1, fill='both')

root.mainloop()

EDIT

Sorry FJSevilla, how could I integrate what you have given me with this example that I have in my application? This style what it does is to put an icon to close the tabs.

    style = ttk.Style()
    style.configure('.',background=self.bg_2)
    style.element_create("close", "image", "img_close",
        ("active", "pressed", "!disabled", "img_closepressed"),
        ("active", "!disabled", "img_closeactive"), border=10, sticky='n')

    style.layout("ButtonNotebook", [("ButtonNotebook.client", {"sticky": ""})])
    style.layout("ButtonNotebook.Tab", [
        ("ButtonNotebook.tab", {"sticky": "", "children":
            [("ButtonNotebook.padding", {"side": "top", "sticky": "",
                                         "children":
                [("ButtonNotebook.focus", {"side": "top", "sticky": "",
                                           "children":
                    [("ButtonNotebook.label", {"side": "left", "sticky": ''}),
                     ("ButtonNotebook.close", {"side": "right", "sticky": 'n'})]
                })]
            })]
        })]
    )

4voto

FJSevilla Points 29084

You can define your own style and specify the color that the selected tab should have. Here is an example.

import tkinter
from tkinter import ttk

root = tkinter.Tk()

style = ttk.Style()
settings = {"TNotebook.Tab": {"configure": {"padding": [5, 1],
                                            "background": "#fdd57e"
                                           },
                              "map": {"background": [("selected", "#C70039"), 
                                                     ("active", "#fc9292")],
                                      "foreground": [("selected", "#ffffff"),
                                                     ("active", "#000000")]

                                     }
                              }
           }  

style.theme_create("mi_estilo", parent="alt", settings=settings)
style.theme_use("mi_estilo")

nb = ttk.Notebook(width=200, height=200)
nb.pressed_index = None
f1 = tkinter.Frame(nb)
f2 = tkinter.Frame(nb)
f3 = tkinter.Frame(nb)
nb.add(f1, text='Pestaña 1')
nb.add(f2, text='Pestaña 2')
nb.add(f3, text='Pestaña 3')
nb.pack(expand=1, fill='both')
root.mainloop()

introducir la descripción de la imagen aquí

However, it is not possible to define these properties for an individual tab, at least I do not know of any way to do so. If you want each tab to have a different color when it is selected, what you can do is to use the event <<NotebookTabChanged>> and dynamically modify the style each time a new tab is selected. There are many ways to do this, for example:

import tkinter
from tkinter import ttk

root = tkinter.Tk()

style = ttk.Style()    
settings = {"TNotebook.Tab": {"configure": {"padding": [5, 1],
                                            "background": "#fdd57e"
                                            }}}  
style.theme_create("mi_estilo", parent="alt", settings=settings)
style.theme_use("mi_estilo")

def on_tab(event):
    global tab_styles
    global style
    nb = event.widget
    tab = nb.tab(nb.select(), "text")
    st = tab_styles[tab]
    style.map('TNotebook.Tab', **st)

tab_styles = {}
nb = ttk.Notebook(width=200, height=200)
nb.pressed_index = None

f1 = tkinter.Frame(nb, background="red")
f2 = tkinter.Frame(nb, background="green")
f3 = tkinter.Frame(nb, background="blue")

nb.add(f1, text="Rojo")
tab_styles["Rojo"] = {"background": [("selected", "red")],
                      "foreground": [("selected", "#ffffff")]
                     }
nb.add(f2, text="Verde")
tab_styles["Verde"] = {"background": [("selected", "green")],
                      "foreground": [("selected", "#ffffff")]
                     }

nb.add(f3, text="Azul")
tab_styles["Azul"] = {"background": [("selected", "blue")],
                      "foreground": [("selected", "#ffffff")]
                     }

nb.pack(expand=1, fill='both')
nb.bind("<<NotebookTabChanged>>", on_tab)

root.mainloop()

introducir la descripción de la imagen aquí

Edition

If you create your own widget deriving from ttk.Notebook, the style of the tabs applies the same, just use the correct name of our widget:

 style.map('ButtonNotebook.Tab', background = [("selected", "#C70039"),
                                               ("active", "#fc9292")],
                                 foreground = [("selected", "#ffffff"),
                                               ("active", "#000000")]
                 ) 

A complete example with a close button on the tabs, using an image, and applying the custom styling for the tab background used above:

import sys
import tkinter as tk
from tkinter import ttk

class ButtonNotebook(ttk.Notebook):
    _initialized = False

    def __init__(self, *args, **kwargs):
        if not self._initialized:
            self._initialize()
            self._inititialized = True

        kwargs["style"] = "ButtonNotebook"
        super().__init__(*args, **kwargs)
        self._active = None

        self.bind("<ButtonPress-1>", self.on_tab_close_press, True)
        self.bind("<ButtonRelease-1>", self.on_tab_close_release)

    def on_tab_close_press(self, event):
        name = self.identify(event.x, event.y)
        if name == "tab_btn_close":
            index = self.index("@%d,%d" % (event.x, event.y))
            self.state(['pressed'])
            self._active = index

    def on_tab_close_release(self, event):
        if not self.instate(['pressed']):
            return None

        name =  self.identify(event.x, event.y)

        if name == "tab_btn_close":
            index = self.index("@%d,%d" % (event.x, event.y))
            if self._active == index:
                self.forget(index)
                self.event_generate("<<NotebookTabClosed>>")

        self.state(["!pressed"])
        self._active = None

    def _initialize(self):
        style = ttk.Style()
        if sys.platform == "win32":
            style.theme_use('winnative')

        self.images = (
            tk.PhotoImage("img_close", data='''
                          R0lGODlhCAAIAMIEAAAAAP/SAP/bNNnZ2f///////////////yH5
                          BAEKAAIALAAAAAAIAAgAAAMUCCAsCmO5OBVl8OKhoV3e9jQOkAAAOw==
                           '''),
            tk.PhotoImage("img_closeactive", data='''
                          R0lGODlhCAAIAMIEAAAAAP/SAP/bNNnZ2f///////////////yH5
                          BAEKAAMALAAAAAAIAAgAAAMPCDA8+gw+GGlVbWKqmwMJADs=
                          ''' ),
            tk.PhotoImage("img_closepressed", data='''
                          R0lGODlhCAAIAMIEAAAAAP/SAP/bNNnZ2f///////////////yH5B
                          AEKAAMALAAAAAAIAAgAAAMPGDE8+gw+GGlVbWKqmwsJADs=
                          ''')
        )

        style.element_create("tab_btn_close", "image", "img_close",
                            ("active", "pressed", "!disabled", "img_closepressed"),
                            ("active", "!disabled", "img_closeactive"), border=8, sticky='')

        style.layout("ButtonNotebook", [("ButtonNotebook.client", {"sticky": "nswe"})])
        style.layout("ButtonNotebook.Tab", [
            ("ButtonNotebook.tab", {
                "sticky": "nswe", 
                "children": [
                    ("ButtonNotebook.padding", {
                        "side": "top", 
                        "sticky": "nswe",
                        "children": [
                            ("ButtonNotebook.focus", {
                                "side": "top", 
                                "sticky": "nswe",
                                "children": [
                                    ("ButtonNotebook.label", {"side": "left", "sticky": ''}),
                                    ("ButtonNotebook.tab_btn_close", {"side": "left", "sticky": ''}),
                                ]
                            })
                        ]
                    })
                ]
            })
        ])

        style.configure("ButtonNotebook.Tab", background="#fdd57e")         
        style.map('ButtonNotebook.Tab', background = [("selected", "#C70039"),
                                                      ("active", "#fc9292")],
                                        foreground = [("selected", "#ffffff"),
                                                      ("active", "#000000")]
                 ) 

if __name__ == "__main__":
    root = tk.Tk()
    nb = ButtonNotebook(width=200, height=200)
    nb.pressed_index = None
    f1 = tk.Frame(nb)
    f2 = tk.Frame(nb)
    f3 = tk.Frame(nb)
    nb.add(f1, text='Pestaña 1')
    nb.add(f2, text='Pestaña 2')
    nb.add(f3, text='Pestaña 3')
    nb.pack(expand=1, fill='both')
    root.mainloop()

introducir la descripción de la imagen aquí

This example is inspired by this answer by Bryan Oakley on the English site.

Note: The images are 8-bit .gifs with a size of 8 x 8 pixels encoded in Base64. They can be loaded from another source, as a file, just as we would do with any image.

0 votes

Thanks FJSevilla, that's just what I was looking for.

0 votes

I have edited the question FJSevilla, because I can't integrate your code with the style I already had.

0 votes

Any ideas? Please?

HolaDevs.com

HolaDevs is an online community of programmers and software lovers.
You can check other people responses or create a new question if you don't find a solution

Powered by:

X