Home > front end >  Make the contents of a tabControl tab scrollable while it still functions as a tab - tkinter
Make the contents of a tabControl tab scrollable while it still functions as a tab - tkinter

Time:01-20

TKINTER - I have a simple GUI which makes use of some tabControl tabs. I would like to make the contents of a tab (multiple widgets) scrollable. I have figured out how to make an entire window with several widgets scrollable using a window inside a canvas, as proposed by many SO-users and other websites. However, if I try this technique with the contents of a tabControl tab, it does not work.

reprex:

from tkinter import *
from tkinter import ttk
import tkinter as tk

# main window
window = tk.Tk()
window.geometry('400x400')
tabControl = ttk.Notebook(window)

# create tabs
tab_A = ttk.Frame(tabControl)
tab_B = ttk.Frame(tabControl)
tab_C = ttk.Frame(tabControl)
tabControl.add(tab_A, text='A')
tabControl.add(tab_B, text='B')
tabControl.add(tab_C, text='C')
tabControl.grid()

# tab A
for i in range(5):
    dir_frame = LabelFrame(tab_A, text=f'LabelFrame {i}', pady=3, padx=5, relief='solid', highlightthickness=5, font=100, fg='darkblue')
    dir_frame.grid(column=0, row=i, columnspan=2, sticky='ew')
    Label(master=dir_frame, text="Text text \t\t\t").grid(row=0, column=0, sticky='w', pady=15)
    Button(master=dir_frame, text="Button").grid(row=0, column=1, sticky='e', padx=5)

# tab B
for i in range(7):
    Label(master=tab_B, text=f"Text {i}\t\t\t\t").grid(row=i, column=0, sticky='w', pady=18)
    chb2 = Checkbutton(tab_B).grid(row=i, column=1, sticky='e', padx=15)

# tab C
scroll = Scrollbar(tab_C)
scroll.grid(column=1, sticky='ns')
text = Text(tab_C, width=45, wrap=WORD, yscrollcommand=scroll.set)
for i in range(5):
    text.insert(END, f'\n{i}.\n')
    text.insert(END, f'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.')
    text.insert(END, f'\n\n\n\n')
text.grid(column=0, row=0, sticky="nesw")
text.configure(state=DISABLED)
scroll.config(command=text.yview)

# run
window.mainloop()

So if I create a main frame and canvas with scrollbar, configure this canvas, create another frame inside the canvas and add that new frame to a window in the canvas (which seems to be the way to do it), it is scrollable but shows up below my tab. Besides, the tab function does not show/hide the content anymore.

gui

This is my code:

from tkinter import *
from tkinter import ttk
import tkinter as tk

# main window
window = tk.Tk()
window.geometry('400x400')
tabControl = ttk.Notebook(window)

# create tabs
tab_A = ttk.Frame(tabControl)
tab_B = ttk.Frame(tabControl)
tab_C = ttk.Frame(tabControl)
tabControl.add(tab_A, text='A')
tabControl.add(tab_B, text='B')
tabControl.add(tab_C, text='C')
tabControl.grid()

# create a main frame
main_frame = Frame(window)
main_frame.columnconfigure(0, weight=1)
main_frame.grid(column=0, row=1, sticky='nsew')

# create a canvas
my_canvas = Canvas(main_frame)
my_canvas.grid(column=0, row=1, sticky='nsew')

# add a scrollbar to the canvas
my_scrollbar = ttk.Scrollbar(main_frame, orient=VERTICAL, command=my_canvas.yview)
my_scrollbar.grid(column=0, row=1, sticky='ens')

# configure the canvas
my_canvas.configure(yscrollcommand=my_scrollbar.set)
my_canvas.bind('<Configure>', lambda e: my_canvas.configure(scrollregion=my_canvas.bbox("all")))

# create another frame inside the canvas
second_frame = Frame(my_canvas)

# add that new frame to a window in the canvas
my_canvas.create_window((0,0), window=second_frame, anchor="nw")

# tab A
for i in range(5):
    dir_frame = LabelFrame(second_frame, text=f'LabelFrame {i}', pady=3, padx=5, relief='solid', highlightthickness=5, font=100, fg='darkblue')
    dir_frame.grid(column=0, row=i, columnspan=2, sticky='ew')
    Label(master=dir_frame, text="Text text \t\t\t").grid(row=0, column=0, sticky='w', pady=15)
    Button(master=dir_frame, text="Button").grid(row=0, column=1, sticky='e', padx=5)

# tab B
for i in range(7):
    Label(master=tab_B, text=f"Text {i}\t\t\t\t").grid(row=i, column=0, sticky='w', pady=18)
    chb2 = Checkbutton(tab_B).grid(row=i, column=1, sticky='e', padx=15)

# tab C
scroll = Scrollbar(tab_C)
scroll.grid(column=1, sticky='ns')
text = Text(tab_C, width=45, wrap=WORD, yscrollcommand=scroll.set)
for i in range(5):
    text.insert(END, f'\n{i}.\n')
    text.insert(END, f'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.')
    text.insert(END, f'\n\n\n\n')
text.grid(column=0, row=0, sticky="nesw")
text.configure(state=DISABLED)
scroll.config(command=text.yview)

# run
window.mainloop()

Tab C, which contains only a text widget, functions like I would like to have A en B too. It is scrollable and the tabs functions like a tab.

Any suggestions?

CodePudding user response:

You've put the canvas in the main frame rather than in a tab. If you want it to be in a tab, it needs to be a child of the frame for a tab.

main_frame = Frame(tab_A)
#                  ^^^^^

Unrelated to the question that was asked, I recommend using pack rather than grid for widgets that are the only child, or one of only a couple of children in a containing widget. It requires a little bit less code and is a bit more foolproof since you don't have to worry about adding weights to the only row and column.

For example, it takes a single line of code to get the notebook to fill the window and to grow and shrink as it resizes:

tabControl.pack(fill="both", expand=True)

The same is true for main_frame since it's the only widget in the tab frame.

main_frame.pack(fill="both", expand=True)

And finally, pack also makes laying out a widget and it's scrollbar very easy:

my_scrollbar.pack(side="right", fill="y")
my_canvas.pack(side="left", fill="both", expand=True)
  •  Tags:  
  • Related