I would like to make a scrollable Frame.
from tkinter import *
from tkinter import ttk
class MainWindow:
def __init__(self):
self.metrics = []
self.content = ttk.Frame(root)
self.v = Scrollbar(root, orient = "vertical")
self.class_label = Label(self.content, text = "labels")
self.A_label = Label(self.content, text = "A")
self.A_entry = Entry(self.content, width = 5)
self.A_input = self.A_entry.get()
self.A_hint = Label(self.content, text = "A")
self.B_label = Label(self.content, text = "B")
self.B_entry = Entry(self.content, width = 5)
self.B_input = self.B_entry.get()
self.B_hint = Label(self.content, text = "B")
self.C = Label(self.content, text = "C")
self.space = Label(self.content, text = "")
self.D_label = Label(self.content, text = "D")
self.D_entry = Entry(self.content, width = 5)
self.D_hint = Label(self.content, text = "D")
self.E_label = Label(self.content, text = "E")
self.E_entry = Entry(self.content, width = 5)
self.E_hint = Label(self.content, text = "D")
self.F_label = Label(self.content, text = "F")
self.F_entry = Entry(self.content, width = 5)
self.F_hint = Label(self.content, text = "F")
self.G_label = Label(self.content, text = "G")
self.G_entry = Entry(self.content, width = 5)
self.G_hint = Label(self.content, text = "G")
self.H_label = Label(self.content, text = "H")
self.H_entry = Entry(self.content, width = 5)
self.H_hint = Label(self.content, text = "H")
self.I_label = Label(self.content, text = "I")
self.I_entry = Entry(self.content, width = 5)
self.I_hint = Label(self.content, text = "I")
self.J_label = Label(self.content, text = "J")
self.J_entry = Entry(self.content, width = 5)
self.J_hint = Label(self.content, text = "J")
self.K_label = Label(self.content, text = "K")
self.K_entry = Entry(self.content, width = 5)
self.K_hint = Label(self.content, text = "K")
self.space2 = Label(self.content, text = "")
self.L_label = Label(self.content, text = "L")
self.L_entry = Entry(self.content, width = 5)
self.content.grid(row = 0, column = 0)
self.v.grid(row = 0, column = 1, sticky = NS)
self.class_label.grid(row = 0, column = 0)
self.A_label.grid(row = 1, column = 0)
self.A_entry.grid(row = 1, column = 1)
self.A_hint.grid(row = 1, column = 3, sticky = "w")
self.B_label.grid(row = 2, column = 0)
self.B_entry.grid(row = 2, column = 1)
self.B_hint.grid(row = 2, column = 3, sticky = "w")
self.C.grid(row = 3, column = 3, sticky = "w")
self.space.grid(row = 4, column = 0)
self.D_label.grid(row = 6, column = 0)
self.D_entry.grid(row = 6, column = 1)
self.D_hint.grid(row = 6, column = 3, sticky = "w")
self.E_label.grid(row = 7, column = 0)
self.E_entry.grid(row = 7, column = 1)
self.E_hint.grid(row = 7, column = 3, sticky = "w")
self.F_label.grid(row = 8, column = 0)
self.F_entry.grid(row = 8, column = 1)
self.F_hint.grid(row = 8, column = 3, sticky = "w")
self.G_label.grid(row = 9, column = 0)
self.G_entry.grid(row = 9, column = 1)
self.G_hint.grid(row = 9, column = 3, sticky = "w")
self.H_label.grid(row = 10, column = 0)
self.H_entry.grid(row = 10, column = 1)
self.H_hint.grid(row = 10, column = 3, sticky = "w", columnspan = 2)
self.I_label.grid(row = 11, column = 0)
self.I_entry.grid(row = 11, column = 1)
self.I_hint.grid(row = 11, column = 3, sticky = "w", columnspan = 2)
self.J_label.grid(row = 12, column = 0)
self.J_entry.grid(row = 12, column = 1)
self.J_hint.grid(row = 12, column = 3, sticky = "w", columnspan = 2)
self.K_label.grid(row = 13, column = 0)
self.K_entry.grid(row = 13, column = 1)
self.K_hint.grid(row = 13, column = 3, sticky = "w", columnspan = 2)
self.space2.grid(row = 14, column = 0)
self.L_label.grid(row = 19, column = 0)
self.L_entry.grid(row = 19, column = 1)
root.mainloop()
if __name__ == '__main__':
root = Tk()
root.title("title")
new_window = MainWindow()
In this first example, the button in the scrollbar does appear but it can't scroll and when I change the window's size, the scrollbar doesn't move.
If I'm not mistaken, I need to create a canvas within my frame and put everything inside this canvas. However, I read that I'm also supposed to put an inner frame inside the canvas. How am I supposed to do it?
So I tried this:
from tkinter import *
from tkinter import ttk
options = ["A", "B", "C", "D", "E", "F", "G", "H"]
root = Tk()
root.title("random software")
content = ttk.Frame(root)
vscrollbar = Scrollbar(content, orient=VERTICAL)
scrollable_canvas = Canvas(content)
inner_frame = Frame(scrollable_canvas)
A_label = Label(content, text = "A")
A_entry = Entry(content, width = 5)
A_hint = Label(content, text = "lorem")
B_label = Label(content, text = "B")
B_entry = Entry(content, width = 5)
B_hint = Label(content, text = "ipsum")
C_label = Label(content, text = "C")
C_entry = Entry(content, width = 5)
C_hint = Label(content, text = "dolor")
D_label = Label(content, text = "D")
D_entry = Entry(content, width = 5)
D_hint = Label(content, text = "sit")
E_label = Label(content, text = "E")
E_entry = Entry(content, width = 5)
E_hint = Label(content, text = "amet")
F_label = Label(content, text = "F")
F_entry = Entry(content, width = 5)
F_hint = Label(content, text = "consectetur")
G_label = Label(content, text = "G")
G_entry = Entry(content, width = 5)
G_hint = Label(content, text = "adipiscing")
H_label = Label(content, text = "H")
H_entry = Entry(content, width = 5)
H_hint = Label(content, text = "elit")
selected_option = StringVar()
selected_option.set(options[0])
option_label = OptionMenu(content, selected_option, *options)
content.grid(row = 1, column = 0)
vscrollbar.grid(row = 1, column = 1, sticky = NS)
option_label.grid(row = 5, column = 1)
A_label.grid(row = 6, column = 0)
A_entry.grid(row = 6, column = 1)
A_hint.grid(row = 6, column = 3, sticky = "w")
B_label.grid(row = 7, column = 0)
B_entry.grid(row = 7, column = 1)
B_hint.grid(row = 7, column = 3, sticky = "w")
C_label.grid(row = 8, column = 0)
C_entry.grid(row = 8, column = 1)
C_hint.grid(row = 8, column = 3, sticky = "w")
D_label.grid(row = 9, column = 0)
D_entry.grid(row = 9, column = 1)
D_hint.grid(row = 9, column = 3, sticky = "w")
E_label.grid(row = 10, column = 0)
E_entry.grid(row = 10, column = 1)
E_hint.grid(row = 10, column = 3, sticky = "w")
F_label.grid(row = 11, column = 0)
F_entry.grid(row = 11, column = 1)
F_hint.grid(row = 11, column = 3, sticky = "w")
G_label.grid(row = 12, column = 0)
G_entry.grid(row = 12, column = 1)
G_hint.grid(row = 12, column = 3, sticky = "w")
H_label.grid(row = 13, column = 0)
H_entry.grid(row = 13, column = 1)
H_hint.grid(row = 13, column = 3, sticky = "w")
root.mainloop()
In this case, the scrollbar isn't extended from top to bottom, it doesn't appear on the right side of the window and when I change the window's size the scrollbar doesn't move. Why isn't it extended on the right side (like the first case) and how to make the scrollbar scrollable?
I saw a few answers with pack but I would like to continue using grid.
CodePudding user response:
Here is a working example of a frame, containing a canvas, containing an "inner_frame" holding a grid of labels.
Included a function that handles scroll events, but this only works if you have a single scrollable area as it locks the scrollwheel to the application, not the frame.
There is also a part (read the comments) to handle resizing of the window.
import tkinter as tk
# mouse wheel scrolling with reduced speed
def on_mouse_wheel(event):
canvas.yview('scroll', int(-1 * event.delta / 120), 'units')
root = tk.Tk()
root.bind('<MouseWheel>', on_mouse_wheel) # bind mousewheel to root, this only works if you have a single scroll area
window_width = 400
window_height = 200
table_columns = 4
table_rows = 30
root.geometry(f'{window_width}x{window_height}')
main_frame = tk.Frame(root)
main_frame.pack(fill=tk.BOTH, expand=1) # frame goes to the left
canvas = tk.Canvas(main_frame)
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
v_scroll = tk.Scrollbar(main_frame, orient=tk.VERTICAL, command=canvas.yview)
v_scroll.pack(side=tk.RIGHT, fill=tk.Y) # scrollbar goes to the right
canvas.configure(yscrollcommand=v_scroll.set)
canvas.bind(
'<Configure>', lambda e: canvas.configure(scrollregion=canvas.bbox(tk.ALL))
) # adjust scrolling area on resize
inside_frame = tk.Frame(canvas) # frame where you put your actual content
canvas.create_window((0, 0), window=inside_frame, anchor=tk.N) # adding the inside_frame to the canvas
# an example grid with some data
for y in range(table_rows):
for x in range(table_columns):
tk.Label(inside_frame, text=f'{y}:{x}', borderwidth=1, relief=tk.SOLID, width=10).grid(column=x, row=y)
root.mainloop()
This is the way I handle scrollbars for all my GUI applications and it seems to be the only way if you want to scroll widgets other then a textbox or a treeview.
Conclusion: Use the pack manager to arrange the main frame, canvas and inner frame and put all your content inside the inner frame with whatever window manager you like.
UPDATE: Your modified code from the first example I removed ttk as it is not needed and refactored the coded a bit.
import tkinter as tk
class MainWindow:
def __init__(self, root: tk.Tk):
self.root = root
self.root.title("title")
self.root.geometry('100x200')
self.main_frame = tk.Frame(root)
self.main_frame.pack(fill=tk.Y, expand=1)
self.canvas = tk.Canvas(self.main_frame, width=100)
self.canvas.pack(side=tk.LEFT, fill=tk.Y)
self.v = tk.Scrollbar(self.main_frame, orient=tk.VERTICAL, command=self.canvas.yview)
self.v.pack(side=tk.RIGHT, fill=tk.Y)
self.canvas.configure(yscrollcommand=self.v.set)
self.canvas.bind('<Configure>', lambda e: self.canvas.configure(scrollregion=self.canvas.bbox(tk.ALL)))
self.inner_frame = tk.Frame(self.canvas)
self.canvas.create_window((0, 0), window=self.inner_frame, anchor=tk.NW)
self.class_label = tk.Label(self.inner_frame, text="labels")
self.A_label = tk.Label(self.inner_frame, text="A")
self.A_entry = tk.Entry(self.inner_frame, width=5)
self.A_input = self.A_entry.get()
self.A_hint = tk.Label(self.inner_frame, text="A")
self.B_label = tk.Label(self.inner_frame, text="B")
self.B_entry = tk.Entry(self.inner_frame, width=5)
self.B_input = self.B_entry.get()
self.B_hint = tk.Label(self.inner_frame, text="B")
self.C = tk.Label(self.inner_frame, text="C")
self.space = tk.Label(self.inner_frame, text="")
self.D_label = tk.Label(self.inner_frame, text="D")
self.D_entry = tk.Entry(self.inner_frame, width=5)
self.D_hint = tk.Label(self.inner_frame, text="D")
self.E_label = tk.Label(self.inner_frame, text="E")
self.E_entry = tk.Entry(self.inner_frame, width=5)
self.E_hint = tk.Label(self.inner_frame, text="D")
self.F_label = tk.Label(self.inner_frame, text="F")
self.F_entry = tk.Entry(self.inner_frame, width=5)
self.F_hint = tk.Label(self.inner_frame, text="F")
self.G_label = tk.Label(self.inner_frame, text="G")
self.G_entry = tk.Entry(self.inner_frame, width=5)
self.G_hint = tk.Label(self.inner_frame, text="G")
self.H_label = tk.Label(self.inner_frame, text="H")
self.H_entry = tk.Entry(self.inner_frame, width=5)
self.H_hint = tk.Label(self.inner_frame, text="H")
self.I_label = tk.Label(self.inner_frame, text="I")
self.I_entry = tk.Entry(self.inner_frame, width=5)
self.I_hint = tk.Label(self.inner_frame, text="I")
self.J_label = tk.Label(self.inner_frame, text="J")
self.J_entry = tk.Entry(self.inner_frame, width=5)
self.J_hint = tk.Label(self.inner_frame, text="J")
self.K_label = tk.Label(self.inner_frame, text="K")
self.K_entry = tk.Entry(self.inner_frame, width=5)
self.K_hint = tk.Label(self.inner_frame, text="K")
self.space2 = tk.Label(self.inner_frame, text="")
self.L_label = tk.Label(self.inner_frame, text="L")
self.L_entry = tk.Entry(self.inner_frame, width=5)
self.class_label.grid(row=0, column=0)
self.A_label.grid(row=1, column=0)
self.A_entry.grid(row=1, column=1)
self.A_hint.grid(row=1, column=3, sticky="w")
self.B_label.grid(row=2, column=0)
self.B_entry.grid(row=2, column=1)
self.B_hint.grid(row=2, column=3, sticky="w")
self.C.grid(row=3, column=3, sticky="w")
self.space.grid(row=4, column=0)
self.D_label.grid(row=6, column=0)
self.D_entry.grid(row=6, column=1)
self.D_hint.grid(row=6, column=3, sticky="w")
self.E_label.grid(row=7, column=0)
self.E_entry.grid(row=7, column=1)
self.E_hint.grid(row=7, column=3, sticky="w")
self.F_label.grid(row=8, column=0)
self.F_entry.grid(row=8, column=1)
self.F_hint.grid(row=8, column=3, sticky="w")
self.G_label.grid(row=9, column=0)
self.G_entry.grid(row=9, column=1)
self.G_hint.grid(row=9, column=3, sticky="w")
self.H_label.grid(row=10, column=0)
self.H_entry.grid(row=10, column=1)
self.H_hint.grid(row=10, column=3, sticky="w", columnspan=2)
self.I_label.grid(row=11, column=0)
self.I_entry.grid(row=11, column=1)
self.I_hint.grid(row=11, column=3, sticky="w", columnspan=2)
self.J_label.grid(row=12, column=0)
self.J_entry.grid(row=12, column=1)
self.J_hint.grid(row=12, column=3, sticky="w", columnspan=2)
self.K_label.grid(row=13, column=0)
self.K_entry.grid(row=13, column=1)
self.K_hint.grid(row=13, column=3, sticky="w", columnspan=2)
self.space2.grid(row=14, column=0)
self.L_label.grid(row=19, column=0)
self.L_entry.grid(row=19, column=1)
self.root.mainloop()
if __name__ == '__main__':
new_window = MainWindow(tk.Tk())
CodePudding user response:
In first code. You have typo and missing error. self.C_label
, self.K_label
and self.L_label
. Also grid is missing for self.C_label
and self.L_label
Edit: Some grid in rows are not probably between rows 4 to 13. I had to change this.
from tkinter import *
from tkinter import ttk
class MainWindow:
def __init__(self):
self.metrics = []
self.content = ttk.Frame(root)
self.v = Scrollbar(root, orient = "vertical")
self.class_label = Label(self.content, text = "labels")
self.A_label = Label(self.content, text = "A")
self.A_entry = Entry(self.content, width = 5)
self.A_input = self.A_entry.get()
self.A_hint = Label(self.content, text = "A")
self.B_label = Label(self.content, text = "B")
self.B_entry = Entry(self.content, width = 5)
self.B_input = self.B_entry.get()
self.B_hint = Label(self.content, text = "B")
self.C_label = Label(self.content, text = "C")
self.C_entry = Entry(self.content, width = 5)
self.C_input = self.C_entry.get()
self.C_hint = Label(self.content, text = "C")
self.space = Label(self.content, text = "")
self.D_label = Label(self.content, text = "D")
self.D_entry = Entry(self.content, width = 5)
self.D_hint = Label(self.content, text = "D")
self.E_label = Label(self.content, text = "E")
self.E_entry = Entry(self.content, width = 5)
self.E_hint = Label(self.content, text = "E")
self.F_label = Label(self.content, text = "F")
self.F_entry = Entry(self.content, width = 5)
self.F_hint = Label(self.content, text = "F")
self.G_label = Label(self.content, text = "G")
self.G_entry = Entry(self.content, width = 5)
self.G_hint = Label(self.content, text = "G")
self.H_label = Label(self.content, text = "H")
self.H_entry = Entry(self.content, width = 5)
self.H_hint = Label(self.content, text = "H")
self.I_label = Label(self.content, text = "I")
self.I_entry = Entry(self.content, width = 5)
self.I_hint = Label(self.content, text = "I")
self.J_label = Label(self.content, text = "J")
self.J_entry = Entry(self.content, width = 5)
self.J_hint = Label(self.content, text = "J")
self.K_label = Label(self.content, text = "K")
self.K_entry = Entry(self.content, width = 5)
self.K_hint = Label(self.content, text = "K")
self.space2 = Label(self.content, text = "")
self.L_label = Label(self.content, text = "L")
self.L_entry = Entry(self.content, width = 5)
self.L_hint = Label(self.content, text = "L")
self.content.grid(row = 0, column = 0)
self.v.grid(row = 0, column = 1, sticky = NS)
self.class_label.grid(row = 0, column = 0)
self.A_label.grid(row = 1, column = 0)
self.A_entry.grid(row = 1, column = 1)
self.A_hint.grid(row = 1, column = 3, sticky = "w")
self.B_label.grid(row = 2, column = 0)
self.B_entry.grid(row = 2, column = 1)
self.B_hint.grid(row = 2, column = 3, sticky = "w")
self.C_label.grid(row = 3, column = 0)
self.C_entry.grid(row = 3, column = 1)
self.C_hint.grid(row = 3, column = 3, sticky = "w")
self.space.grid(row = 4, column = 0)
self.D_label.grid(row = 5, column = 0)
self.D_entry.grid(row = 5, column = 1)
self.D_hint.grid(row = 5, column = 3, sticky = "w")
self.E_label.grid(row = 6, column = 0)
self.E_entry.grid(row = 6, column = 1)
self.E_hint.grid(row = 6, column = 3, sticky = "w")
self.F_label.grid(row = 7, column = 0)
self.F_entry.grid(row = 7, column = 1)
self.F_hint.grid(row = 7, column = 3, sticky = "w")
self.G_label.grid(row = 8, column = 0)
self.G_entry.grid(row = 8, column = 1)
self.G_hint.grid(row = 8, column = 3, sticky = "w")
self.H_label.grid(row = 9, column = 0)
self.H_entry.grid(row = 9, column = 1)
self.H_hint.grid(row = 9, column = 3, sticky = "w", columnspan = 2)
self.I_label.grid(row = 10, column = 0)
self.I_entry.grid(row = 10, column = 1)
self.I_hint.grid(row = 10, column = 3, sticky = "w", columnspan = 2)
self.J_label.grid(row = 11, column = 0)
self.J_entry.grid(row = 11, column = 1)
self.J_hint.grid(row = 11, column = 3, sticky = "w", columnspan = 2)
self.K_label.grid(row = 12, column = 0)
self.K_entry.grid(row = 12, column = 1)
self.K_hint.grid(row = 12, column = 3, sticky = "w", columnspan = 2)
self.space2.grid(row = 13, column = 0)
self.L_label.grid(row = 14, column = 0)
self.L_entry.grid(row = 14, column = 1)
self.L_hint.grid(row = 14, column = 3, sticky = "w", columnspan = 2)
root.mainloop()
if __name__ == '__main__':
root = Tk()
root.title("title")
new_window = MainWindow()
Result first code:
This is the first code you wrote. Some of them are Label C, D and L. Look at screenshot in below..
Result first code: